接口调试

This commit is contained in:
BianLzhaoMin 2025-07-02 16:37:01 +08:00
parent 6e407d15cd
commit 0fde5f9126
10 changed files with 849 additions and 274 deletions

View File

@ -4,4 +4,5 @@ VITE_APP_content = 机器人
VITE_APP_baseApiURL = https://s.dumogu.top/api
# 路由的base api
VITE_APP_routeBasePath = /
VITE_APP_biuldBase = /
VITE_APP_biuldBase = /
VITE_APP_imgPreviewUrl = http://192.168.0.38:21999/robot

View File

@ -19,6 +19,11 @@ export const addMarkerApi = (data) => {
return service.post('/robot-screen-api/robot/instruct/addPoint', data)
}
// 删除巡视点位接口
export const deleteMarkerApi = (data) => {
return service.post('/robot-screen-api/robot/instruct/delPoint', data)
}
// 获取全部已经添加的点位
export const getMarkerListAllApi = (data) => {
return service.post('/robot-screen-api/robot/instruct/getPointList', data)
@ -59,7 +64,57 @@ export const addPersonApi = (data) => {
return service.post('/robot-screen-api/robot/sbdUser/addSbdUser', data)
}
// 获取人员列表
export const getPersonListApi = (data) => {
return service.get('/robot-screen-api/robot/instruct/list', { params: data })
// 修改人员
export const updatePersonApi = (data) => {
return service.post('/robot-screen-api/robot/sbdUser/updateSbdUser', data)
}
// 删除人员
export const deletePersonApi = (data) => {
return service.post('/robot-screen-api/robot/sbdUser/delUser', data)
}
// 人员详情
export const getDetailsApi = (data) => {
return service.get('/robot-screen-api/robot/sbdUser/getDetails', { params: data })
}
// 现场定点巡检拍照 一级列表
export const getImageLimitApi = (data) => {
return service.get('/robot-screen-api/robot/image/getImageLimit', { params: data })
}
// 现场定点巡检拍照 二级页面列表
export const getImagePageListApi = (data) => {
return service.get('/robot-screen-api/robot/image/getImagePageList', { params: data })
}
// 智能对比一级页面数据
export const getTaskStatisticsApi = (data = {}) => {
return service.get('/robot-screen-api/robot/image/getTaskStatistics', { params: data })
}
// 操作面板内上传音频文件接口
export const uploadAudioApi = (data) => {
return service.post('/robot-screen-api/robot/instruct/addVideoFile', data)
}
// 操作面板内获取音频文件信息
export const getAudioInfoApi = (data) => {
return service.post('/robot-screen-api/robot/instruct/getVideoList', data)
}
// 人员动态 一级页面列表接口
export const getUserListLimitApi = (data) => {
return service.post('/robot-screen-api/robot/sbdUser/getUserListLimit', data)
}
// 人员动态 二级级页面人员动态列表接口
export const getUserListPageApi = (data) => {
return service.get('/robot-screen-api/robot/sbdUser/getUserList', { params: data })
}
// 人员动态 二级级页面人员信息列表接口
export const getUserInfoListApi = (data) => {
return service.get('/robot-screen-api/robot/sbdUser/list', { params: data })
}

View File

@ -19,13 +19,15 @@ export const getDeviceUrlApi = (data) => {
export const changeDeviceCameraApi = (data) => {
return service.post('/third-party/PTZ/C_PTZ_Turn?token=' + data.token, data)
}
// 调整设备的摄像机放大图像
export const changeDeviceCameraZoomInApi = (data) => {
return service.post('/third-party/PTZ/C_PTZ_ZoomInPicture?token=' + data.token, data)
}
// 调整设备的摄像机缩小图像
export const changeDeviceCameraZoomOutApi = (data) => {
return service.post('/third-party/PTZ//C_PTZ_ZoomOutPicture?token=' + data.token, data)
return service.post('/third-party/PTZ/C_PTZ_ZoomOutPicture?token=' + data.token, data)
}
// 停止设备缩放

View File

@ -11,41 +11,43 @@
<div class="marquee-track" :style="{ width: trackWidth }">
<div
class="marquee-item"
v-for="item in items"
v-for="item in imgOuterList"
:key="item.id"
:style="{ width: imgWidth + 'px' }"
>
<n-image
:width="imgWidth"
:height="imgHeight"
:src="item.image"
:src="imgPreviewUrl + item.image"
class="item-image"
/>
<div class="marquee-item-title">途径点{{ item.id }}</div>
<div class="marquee-item-time">{{ item.time }}</div>
<div class="type-container" :class="item.status">
{{ item.status === 'arrived' ? '已到达' : '未到达' }}
<div class="marquee-item-title">{{ item?.pointName }}</div>
<div class="marquee-item-time">{{ item?.taskTime }}</div>
<div class="type-container arrived">
<!-- {{ item.status === 'arrived' ? '已到达' : '未到达' }} -->
已到达
</div>
</div>
<!-- 克隆元素实现无缝循环 -->
<div
class="marquee-item"
v-for="item in items"
v-for="item in imgOuterList"
:key="`clone-${item.id}`"
v-if="items.length > 4"
v-if="imgOuterList.length > 4"
:style="{ width: imgWidth + 'px' }"
>
<n-image
:width="imgWidth"
:height="imgHeight"
:src="item.image"
:src="imgPreviewUrl + item.image"
object-fit="cover"
class="item-image"
/>
<div class="marquee-item-title">途径点{{ item.id }}</div>
<div class="marquee-item-time">{{ item.time }}</div>
<div class="type-container" :class="item.status">
{{ item.status === 'arrived' ? '已到达' : '未到达' }}
<div class="marquee-item-title">{{ item?.pointName }}</div>
<div class="marquee-item-time">{{ item?.taskTime }}</div>
<div class="type-container arrived">
<!-- {{ item.status === 'arrived' ? '已到达' : '未到达' }} -->
已到达
</div>
</div>
</div>
@ -83,16 +85,31 @@
size="small"
label-placement="left"
style="margin-top: 10px"
:model="innerQueryParams"
>
<n-form-item>
<n-date-picker type="daterange" clearable style="width: 240px" />
</n-form-item>
<n-form-item>
<n-input placeholder="输入途经点" clearable style="width: 240px" />
<n-date-picker
clearable
type="daterange"
style="width: 240px"
v-model:formatted-value="ranger"
formatted-value="['YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss']"
/>
</n-form-item>
<n-button type="info"> 查询</n-button>
<n-button color="#6E90A9" style="margin-left: 8px"> 重置 </n-button>
<n-form-item>
<n-input
clearable
placeholder="输入途经点"
style="width: 240px"
v-model:value="innerQueryParams.pointName"
/>
</n-form-item>
<n-button type="info" @click="getImageInnerData"> 查询</n-button>
<n-button color="#6E90A9" style="margin-left: 8px" @click="onHandleReset">
重置
</n-button>
</n-form>
<!-- 内容区域 -->
@ -100,7 +117,7 @@
<!-- 分页 -->
<div
v-for="item in 7"
v-for="item in imgInnerList"
:key="item"
class="table-item"
:style="{ width: tableItemWidth + 'px' }"
@ -110,22 +127,24 @@
height="170"
object-fit="cover"
class="item-image"
src="https://img1.baidu.com/it/u=3294211986,2810142789&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500"
:src="imgPreviewUrl + item?.image"
/>
<div class="table-item-title">
<span>途经点</span>
<span>2025-06-01 10:00:00</span>
<span>{{ item?.pointName }}</span>
<span>{{ item?.taskTime }}</span>
</div>
</div>
</div>
<div style="margin-top: 10px; display: flex; justify-content: flex-end">
<n-pagination
v-model:page="page"
v-model:page-size="pageSize"
:page-count="100"
show-size-picker
:item-count="innerTotal"
:page-sizes="[10, 20, 30, 40]"
@update:page="pageNumChange"
@update:page-size="pageSizeChange"
v-model:page="innerQueryParams.pageNum"
v-model:page-size="innerQueryParams.pageSize"
/>
</div>
</n-card>
@ -135,62 +154,31 @@
<script setup scoped>
import TitleBackground from '@/components/TitleBackground/index.vue'
import { ref, onMounted, onUnmounted } from 'vue'
import { getImageLimitApi, getImagePageListApi } from '@/api/home'
import imgSrc from '@/assets/demo.png'
const morePanelVisible = ref(false)
const searchForm = ref({})
const marqueeContainer = ref(null)
const trackWidth = ref('auto')
const imageHeight = ref('0px')
const itemMargin = 10 // 10px
const page = ref(1)
const pageSize = ref(10)
const imgHeight = ref(0)
const imgWidth = ref(0)
const tableItemWidth = ref(0)
const marqueeOuterRef = ref(null)
const tableContainerRef = ref(null)
const imgOuterList = ref([]) //
const imgInnerList = ref([]) //
const innerTotal = ref(0)
const ranger = ref(null)
const imgPreviewUrl = ref('http://192.168.0.38:21999/robot') //
//
const items = ref([
{
id: 1,
time: '14:23:56',
status: 'arrived',
image: imgSrc,
},
{
id: 2,
time: '15:10:22',
status: 'not-arrived',
image: 'https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg',
},
{
id: 1,
time: '14:23:56',
status: 'arrived',
image: imgSrc,
},
{
id: 2,
time: '15:10:22',
status: 'not-arrived',
image: 'https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg',
},
{
id: 1,
time: '14:23:56',
status: 'arrived',
image: imgSrc,
},
{
id: 2,
time: '15:10:22',
status: 'not-arrived',
image: 'https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg',
},
])
const innerQueryParams = ref({
pageNum: 1,
pageSize: 8,
startDay: '', //
endDay: '', //
pointName: '', //
})
//
const setupMarquee = () => {
@ -218,23 +206,61 @@ const setupMarquee = () => {
document.documentElement.style.setProperty('--animation-duration', `${animationDuration}s`)
}
const pageNumChange = (pageNum) => {
innerQueryParams.value.pageNum = pageNum
getImageInnerData()
}
const pageSizeChange = (pageSize) => {
innerQueryParams.value.pageSize = pageSize
getImageInnerData()
}
//
const getImageInnerData = async () => {
const [startDay, endDay] = ranger?.value || []
innerQueryParams.value.startDay = startDay || ''
innerQueryParams.value.endDay = endDay || ''
const { data: res } = await getImagePageListApi(innerQueryParams.value)
imgInnerList.value = res?.rows
innerTotal.value = res?.total
}
//
const onHandleMore = () => {
morePanelVisible.value = true
nextTick(() => {
// setupMarquee()
const tableContainer = tableContainerRef.value
tableItemWidth.value = (tableContainer.clientWidth - 40) / 4
console.log(tableContainer.clientWidth, 'tableContainer')
getImageInnerData()
})
}
//
const getImageLimitData = async () => {
const { data: res } = await getImageLimitApi({})
imgOuterList.value = res?.data
}
//
const onHandleReset = () => {
ranger.value = null
innerQueryParams.value = {
page: 1,
pageSize: 10,
startTime: '',
endTime: '',
pointName: '',
}
getImageInnerData()
}
onMounted(() => {
setupMarquee()
window.addEventListener('resize', setupMarquee)
getImageLimitData()
nextTick(() => {
const container = marqueeContainer.value
imgWidth.value = (container.clientWidth - 40) / 4
@ -380,6 +406,7 @@ onUnmounted(() => {
.table-container {
// height: 75%;
// min-height: 50vh;
display: flex;
flex-wrap: wrap;
color: #fff;

View File

@ -75,34 +75,29 @@
<!-- 上下左右控制按钮 -->
<img
class="arrow-top hand-direction"
src="@/assets/home-imgs/control-2-arrow.png"
alt=""
@click="handleChangeCamera('up')"
src="@/assets/home-imgs/control-2-arrow.png"
/>
<img
class="arrow-right hand-direction"
src="@/assets/home-imgs/control-2-arrow.png"
alt=""
@click="handleChangeCamera('right')"
src="@/assets/home-imgs/control-2-arrow.png"
/>
<img
class="arrow-bottom hand-direction"
src="@/assets/home-imgs/control-2-arrow.png"
alt=""
@click="handleChangeCamera('down')"
src="@/assets/home-imgs/control-2-arrow.png"
/>
<img
class="arrow-left hand-direction"
src="@/assets/home-imgs/control-2-arrow.png"
alt=""
@click="handleChangeCamera('left')"
src="@/assets/home-imgs/control-2-arrow.png"
/>
<!-- 中间的按钮 -->
<div class="row-3-item-1-center">
<img
class="center-icon"
alt=""
:src="isStopLeft ? stopImg : startImg"
@click="handleChangeCamera('stop')"
/>
@ -118,15 +113,13 @@
<div class="add-reduce-btn">
<span>缩放</span>
<img
src="@/assets/home-imgs/control-2-add.png"
alt=""
style="margin-bottom: 2px"
@click="handleChangeZoomCamera('ZoomIn')"
src="@/assets/home-imgs/control-2-add.png"
/>
<img
src="@/assets/home-imgs/control-2-reduce.png"
alt=""
@click="handleChangeZoomCamera('ZoomOut')"
src="@/assets/home-imgs/control-2-reduce.png"
/>
</div>
</div>
@ -135,8 +128,8 @@
<div class="row-3-item-2" ref="container">
<!-- 上下滑动调节 -->
<span
class="up-down-box"
ref="draggable"
class="up-down-box"
@mousedown="startDrag"
:style="{ top: currentTop + 'px' }"
/>
@ -146,35 +139,30 @@
<div class="row-3-item-3">
<!-- 上下左右控制按钮 -->
<img
@click="handleChangeRobot('5')"
class="arrow-top"
@click="handleChangeRobot('5')"
src="@/assets/home-imgs/control-2-arrow.png"
alt=""
/>
<img
@click="handleChangeRobot('8')"
class="arrow-right"
@click="handleChangeRobot('8')"
src="@/assets/home-imgs/control-2-arrow.png"
alt=""
/>
<img
@click="handleChangeRobot('6')"
class="arrow-bottom"
@click="handleChangeRobot('6')"
src="@/assets/home-imgs/control-2-arrow.png"
alt=""
/>
<img
@click="handleChangeRobot('7')"
class="arrow-left"
@click="handleChangeRobot('7')"
src="@/assets/home-imgs/control-2-arrow.png"
alt=""
/>
<div class="row-3-item-1-center">
<img
class="center-icon"
:src="isStopRight ? startImg : stopImg"
alt=""
@click="handleChangeRobot('9')"
/>
</div>
@ -189,10 +177,10 @@
<n-grid-item :span="3">
<div>
<n-radio
:checked="checkedValue === '关'"
value="关"
name="basic-demo"
@change="handleChange1"
:checked="checkedValue === '关'"
>
</n-radio>
@ -201,10 +189,10 @@
<n-grid-item :span="3">
<div>
<n-radio
:checked="checkedValue === '闪烁'"
value="闪烁"
name="basic-demo"
@change="handleChange2"
:checked="checkedValue === '闪烁'"
>
闪烁
</n-radio>
@ -213,10 +201,10 @@
<n-grid-item :span="3">
<div class="row-4-item">
<n-radio
:checked="checkedValue === '运动时闪烁'"
value="运动时闪烁"
name="basic-demo"
@change="handleChange3"
:checked="checkedValue === '运动时闪烁'"
>
运动时闪烁
</n-radio>
@ -243,7 +231,7 @@
:options="selectOptions"
/> -->
<n-select
<!-- <n-select
:options="selectOptions"
size="small"
:dropdown-style="{
@ -251,9 +239,34 @@
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
color: '#fff',
}"
/>
/> -->
<!-- <n-button type="info" size="small">请上传</n-button> -->
<n-upload
:accept="audioMimeTypes"
:show-file-list="false"
:default-upload="false"
@before-upload="beforeUpload"
@change="handleChangeUpload"
v-if="!audioInfo"
>
<n-button type="info" size="small">请上传</n-button>
</n-upload>
<n-popselect
trigger="click"
v-model:value="audioValue"
:options="audioOptions"
@update:value="handleChangeAudio"
v-else
>
<n-button type="info" size="small">
{{ audioInfo?.fileName }}
</n-button>
</n-popselect>
</div>
<div style="margin-top: 16px">
<!-- <div style="margin-top: 16px">
<n-radio
:checked="checkedValue === '循环播放'"
value="循环播放"
@ -262,7 +275,7 @@
>
循环播放
</n-radio>
</div>
</div> -->
</div>
</n-grid-item>
<n-grid-item :span="6">
@ -277,7 +290,7 @@
/>
</div>
</div>
<div class="row-5-item-1" style="margin-top: 16px">
<!-- <div class="row-5-item-1" style="margin-top: 16px">
<img src="@/assets/home-imgs/control-3-video.png" alt="" />
<div style="width: 60%">
<n-slider
@ -286,7 +299,7 @@
:format-tooltip="formatTooltip"
/>
</div>
</div>
</div> -->
</div>
</n-grid-item>
</n-grid>
@ -303,19 +316,21 @@
</n-grid-item>
</n-grid>
</div>
<n-modal v-model:show="selectPlayType"> 播放类型 </n-modal>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ref, onMounted, h } from 'vue'
import {
changeDeviceCameraApi,
stopDeviceCameraApi,
changeDeviceCameraApi,
stopDeviceCameraZoomApi,
changeDeviceCameraZoomInApi,
changeDeviceCameraZoomOutApi,
stopDeviceCameraZoomApi,
} from '@/utils/initLogin'
import { handleRobotActionApi } from '@/api/home'
import { handleRobotActionApi, uploadAudioApi, getAudioInfoApi } from '@/api/home'
import { useRobotDataStore } from '@/store/robot'
import { getRobotTokenFn, getRobotDeviceListFn } from '@/utils/getRobotInfo.js'
import {
@ -324,16 +339,17 @@ import {
BatteryFullSharp,
BatteryChargingSharp,
} from '@vicons/ionicons5'
import { useMessage } from 'naive-ui'
import { useMessage, useDialog, NRadio, NRadioGroup, NInputNumber, NSpace } from 'naive-ui'
import { debounce } from 'lodash'
import stopImg from '@/assets/home-imgs/control-2-stop.png'
import startImg from '@/assets/home-imgs/control-2-start.png'
const dialog = useDialog()
const message = useMessage()
const robotData = useRobotDataStore()
const isStopLeft = ref(true)
const isStopRight = ref(false)
const robotData = useRobotDataStore()
const message = useMessage()
const upDownHeight = ref(30)
const container = ref(null)
const draggable = ref(null)
@ -347,17 +363,79 @@ const deviceToken = ref('')
const deviceInfo = ref({})
const isZoom = ref(false)
const robotBaseInfo = ref({})
const selectOptions = ref([
const selectPlayType = ref(false)
const audioValue = ref('')
const audioInfo = ref(null)
const audioMimeTypes = ref(
'.pcm,.wav,.aac,audio/wav,audio/x-wav,audio/x-aiff,audio/aac,audio/x-aac',
)
const audioOptions = ref([
{
label: '录音1',
value: '录音1',
label: '播放',
value: '播放',
},
{
label: '录音2',
value: '录音2',
label: '删除',
value: '删除',
},
{
label: '暂停',
value: '暂停',
},
])
const beforeUpload = ({ file }) => {
const allowedExtensions = ['.pcm', '.wav', '.aac']
const fileName = file.name.toLowerCase()
const isValid = allowedExtensions.some((ext) => fileName.endsWith(ext))
if (!isValid) {
message.error('只能上传 PCM、WAV、AAC 格式的音频文件')
return false
}
return true
}
//
const handleChangeUpload = async (e) => {
const file = e.file
const formData = new FormData()
formData.append('file', file.file)
formData.append('puid', deviceInfo.value?.puId)
const { data: res } = await uploadAudioApi(formData)
if (res.code == 200) {
message.success('上传音频文件成功')
getAudioInfo()
} else {
message.error('上传音频文件失败')
}
}
//
const getAudioInfo = async () => {
const { data: res } = await getAudioInfoApi({
puid: deviceInfo.value?.puId,
})
if (res.code == 200) {
audioInfo.value = res?.data
}
}
const handleChangeAudio = (e) => {
// console.log(e, '')
if (e === '播放') {
// showPlayTypeDialog()
selectPlayType.value = true
}
}
// (0-100)
const currentHeight = computed(() => {
if (!container.value) return 0
@ -431,9 +509,9 @@ const formatTooltip = (value) => {
const handleStopDeviceCameraZoom = async () => {
isZoom.value = false
const res = await stopDeviceCameraZoomApi({
idx: 0,
token: deviceToken.value,
puid: '201115200268437643',
idx: 0,
})
console.log(res, '停止位置---')
@ -444,7 +522,6 @@ const handleChangeCamera = debounce(async (motion) => {
if (motion === 'stop' && isZoom.value) {
isStopLeft.value = true
handleStopDeviceCameraZoom()
return
}
if (motion === 'stop') {
@ -517,7 +594,6 @@ const getRobotBaseInfo = async () => {
puId: deviceInfo.value?.puId,
type: '1',
})
console.log(res, '获取机器人的基础信息---')
robotBaseInfo.value = res.data
}
@ -527,8 +603,8 @@ onMounted(async () => {
const device = await getRobotDeviceListFn() //
deviceToken.value = token
deviceInfo.value = device
getRobotBaseInfo()
getAudioInfo()
})
</script>
<style lang="scss" scoped>

View File

@ -83,7 +83,7 @@ import { NIcon } from 'naive-ui'
import { throttle } from 'lodash'
import { AddCircleOutline, RemoveCircleOutline } from '@vicons/ionicons5'
import { getRobotDeviceListFn, getRobotMapInfoFn } from '@/utils/getRobotInfo'
import { getMarkerListAllApi } from '@/api/home'
import { getMarkerListAllApi, deleteMarkerApi } from '@/api/home'
import { useMessage } from 'naive-ui'
const modalTitle = ref('预置位配置') //
@ -424,6 +424,7 @@ const handleModifyPoint = () => {
markerAngle: point.markerAngle,
markerPreset: point.markerPreset,
id: point.id,
isAdd: point.isAdd,
}
emits('onHandleAddMarker', markerInfo)
}
@ -431,9 +432,20 @@ const handleModifyPoint = () => {
}
//
const handleDeletePoint = () => {
const handleDeletePoint = async () => {
if (selectedPointIndex.value >= 0) {
devicePoints.value.splice(selectedPointIndex.value, 1)
if (devicePoints.value[selectedPointIndex.value].isAdd) {
devicePoints.value.splice(selectedPointIndex.value, 1)
} else {
//
const { data: res } = await deleteMarkerApi({
id: devicePoints.value[selectedPointIndex.value].id,
})
if (res.code == 200) {
//
devicePoints.value.splice(selectedPointIndex.value, 1)
}
}
}
closeContextMenu()
}
@ -450,6 +462,7 @@ const addDevicePoint = (x, y, y1) => {
markerName: '',
markerAngle: '', //
markerPreset: '', //
isAdd: true,
})
}
@ -470,6 +483,7 @@ const handlePointClick = (point, index) => {
markerName: point.markerName,
markerAngle: point.markerAngle,
markerPreset: point.markerPreset,
isAdd: point.isAdd,
}
emits('onHandleAddMarker', markerInfo)
}
@ -501,6 +515,7 @@ const getMarkerListAll = async () => {
markerY1: logicalY1,
markerName: item.pointName,
markerAngle: item.theta,
isAdd: false,
})
})
}

View File

@ -21,21 +21,21 @@
<n-grid
x-gap="12"
:cols="4"
v-for="item in 10"
:key="item"
v-for="(item, index) in outerUserList"
:key="index"
class="person-tb-content-grid"
:class="{ active: item % 2 === 0 }"
:class="{ active: index % 2 === 0 }"
>
<!-- 网格内容保持不变 -->
<n-gi>
<div class="person-item">16:30:30</div>
<div class="person-item">{{ item?.createTime.split(' ')[1] }}</div>
</n-gi>
<n-gi>
<div class="person-item">
<n-image
width="60"
height="50"
src="https://img1.baidu.com/it/u=3294211986,2810142789&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500"
:src="imgPreviewUrl + item?.dataImage"
>
<template #error>
<n-icon :size="100" color="lightGrey">
@ -46,10 +46,10 @@
</div>
</n-gi>
<n-gi>
<div class="person-item">张三</div>
<div class="person-item">{{ item?.userName || '-' }}</div>
</n-gi>
<n-gi>
<div class="person-item">电工</div>
<div class="person-item">{{ item?.workType || '-' }}</div>
</n-gi>
</n-grid>
</div>
@ -102,25 +102,43 @@
v-if="activeIndex === 0"
>
<n-form-item>
<n-date-picker type="daterange" clearable style="width: 240px" />
<n-date-picker
clearable
type="daterange"
style="width: 240px"
v-model:formatted-value="ranger_1"
formatted-value="['YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss']"
/>
</n-form-item>
<n-form-item>
<n-input placeholder="输入途经点" clearable style="width: 240px" />
<n-input
clearable
placeholder="人员姓名"
style="width: 240px"
v-model:value="innerQueryParams_1.userName"
/>
</n-form-item>
<n-form-item>
<n-input placeholder="类型" clearable style="width: 240px" />
<n-input
clearable
placeholder="工种"
style="width: 240px"
v-model:value="innerQueryParams_1.workType"
/>
</n-form-item>
<n-button type="info"> 查询</n-button>
<n-button color="#6E90A9" style="margin-left: 8px"> 重置 </n-button>
<n-button type="info" @click="getUserListPageFn"> 查询</n-button>
<n-button color="#6E90A9" style="margin-left: 8px" @click="onHandleReset_1">
重置
</n-button>
</n-form>
<!-- 内容区域 -->
<n-data-table
:data="data"
:data="tableData_1"
:scroll-x="10"
flex-height
:columns="columns"
:columns="tableColumns_1"
:style="{ height: `70%` }"
v-if="activeIndex === 0"
/>
@ -134,17 +152,35 @@
v-if="activeIndex === 1"
>
<n-form-item>
<n-date-picker type="daterange" clearable style="width: 240px" />
<n-date-picker
clearable
type="daterange"
style="width: 240px"
v-model:formatted-value="ranger_2"
formatted-value="['YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss']"
/>
</n-form-item>
<n-form-item>
<n-input placeholder="输入途经点" clearable style="width: 240px" />
<n-input
clearable
placeholder="人员姓名"
style="width: 240px"
v-model:value="innerQueryParams_2.userName"
/>
</n-form-item>
<n-form-item>
<n-input placeholder="类型" clearable style="width: 240px" />
<n-input
clearable
placeholder="工种"
style="width: 240px"
v-model:value="innerQueryParams_2.workType"
/>
</n-form-item>
<n-button type="info"> 查询</n-button>
<n-button color="#6E90A9" style="margin-left: 8px"> 重置 </n-button>
<n-button type="info" @click="getProjectPersonList"> 查询</n-button>
<n-button color="#6E90A9" style="margin-left: 8px" @click="onHandleReset_2">
重置
</n-button>
<n-button type="info" style="margin-left: 10px" @click="onHandleAddPerson">
新增项目部人员
</n-button>
@ -152,22 +188,37 @@
<!-- 内容区域 -->
<n-data-table
:data="data"
:scroll-x="10"
flex-height
:columns="columns"
:scroll-x="10"
:data="tableData_2"
:columns="tableColumns_2"
:style="{ height: `70%` }"
v-if="activeIndex === 1"
/>
<div style="margin-top: 10px; display: flex; justify-content: flex-end">
<n-pagination
:page-count="100"
show-size-picker
v-model:page="page"
v-if="activeIndex === 0"
v-model:page-size="pageSize"
:item-count="tableTotal_1"
:page-sizes="[10, 20, 30, 40]"
v-model:page="innerQueryParams_1.pageNum"
v-model:page-size="innerQueryParams_1.pageSize"
@update:page="onHandlePage_1Change"
@update:page-size="onHandlePageSize_1Change"
/>
</div>
<div style="margin-top: 10px; display: flex; justify-content: flex-end">
<n-pagination
show-size-picker
v-if="activeIndex === 1"
:item-count="tableTotal_2"
:page-sizes="[10, 20, 30, 40]"
v-model:page="innerQueryParams_2.pageNum"
v-model:page-size="innerQueryParams_2.pageSize"
@update:page="onHandlePage_2Change"
@update:page-size="onHandlePageSize_2Change"
/>
</div>
</n-card>
@ -187,7 +238,7 @@
style="font-size: 20px; font-weight: 600; letter-spacing: 2px"
gradient="linear-gradient(90deg, #CDF7FD 0%, #DAFAFE 20%, #CDF7FD 40%, #DAFAFE 60%, #CDF7FD 80%, #DAFAFE 100%)"
>
新增项目部人员
{{ addOrEditPersonForm.id ? '编辑项目部人员' : '新增项目部人员' }}
</n-gradient-text>
<img
@ -214,6 +265,13 @@
v-model:value="addOrEditPersonForm.userName"
/>
</n-form-item>
<n-form-item label="身份证" path="idCard">
<n-input
clearable
placeholder="身份证"
v-model:value="addOrEditPersonForm.idCard"
/>
</n-form-item>
<n-form-item label="联系方式">
<n-input
@ -237,15 +295,19 @@
<n-form-item label="性别">
<n-radio-group v-model:value="addOrEditPersonForm.sex">
<n-radio value="1"></n-radio>
<n-radio value="2"></n-radio>
<n-radio value="0"></n-radio>
</n-radio-group>
</n-form-item>
<n-form-item label="人脸照片" path="facePhoto">
<n-upload
:max="1"
:default-upload="false"
list-type="image-card"
accept=".jpg,.jpeg,.png"
@preview="handlePreview"
@before-upload="beforeUpload"
:default-file-list="previewFileList"
v-model:file-list="addOrEditPersonForm.facePhoto"
>
</n-upload>
@ -258,24 +320,38 @@
</n-card>
</n-modal>
<n-modal preset="card" v-model:show="showModal" style="width: 600px" title="一张很酷的图片">
<n-modal preset="card" v-model:show="showModal" style="width: 600px">
<img :src="previewImageUrl" style="width: 100%" />
</n-modal>
</template>
<script setup>
import { NTag, NImage, useMessage } from 'naive-ui'
import { NButton, NImage, useMessage, useDialog } from 'naive-ui'
import TitleBackground from '@/components/TitleBackground/index.vue'
import { ref, onMounted, onUpdated, nextTick, h } from 'vue'
import { addPersonApi, getPersonListApi } from '@/api/home'
import {
addPersonApi,
getUserInfoListApi,
getUserListLimitApi,
getUserListPageApi,
getDetailsApi,
updatePersonApi,
deletePersonApi,
} from '@/api/home'
const morePanelVisible = ref(false) //
const addPersonPanelVisible = ref(false) //
const addPersonFormRef = ref(null) //
const contentContainer = ref(null) //
const shouldScroll = ref(false) //
const activeIndex = ref(0) // tab
const activeIndex = ref(1) // tab
const message = useMessage()
const dialog = useDialog()
const outerUserList = ref([]) //
const imgPreviewUrl = ref('http://192.168.0.38:21999/robot') //
const ranger_1 = ref(null) //
const ranger_2 = ref(null) //
const previewFileList = ref([]) //
const addOrEditPersonForm = ref({
userName: '', //
mobile: '', //
@ -283,6 +359,8 @@ const addOrEditPersonForm = ref({
age: '', //
sex: '', //
workType: '', //
id: null, // id
idCard: '', //
})
const tapsBtns = ref([
@ -297,96 +375,25 @@ const tapsBtns = ref([
//
const addProjectPersonFormRules = ref({
userName: [{ required: true, message: '请输入姓名' }],
idCard: [
{ required: true, message: '请输入姓名' },
{
pattern: /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[0-1])\d{3}[0-9Xx]$/,
message: '请输入正确的身份证号',
},
],
mobile: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' }],
facePhoto: [{ required: true, message: '请上传人脸照片' }],
})
const previewFileList = ref([])
//
const getUserListLimitFn = async () => {
const { data: res } = await getUserListLimitApi({})
const page = ref(1)
const pageSize = ref(10)
//
const columns = ref([
{
title: '日期时间',
key: 'age',
align: 'center',
},
{
title: '图片',
key: 'tags',
align: 'center',
render(row) {
const tags = row.tags.map((tagKey) => {
return h(
NImage,
{
width: '60px',
height: '60px',
src: 'https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg',
fit: 'cover',
},
{
default: () => tagKey,
},
)
})
return tags
},
},
{
title: '姓名',
key: 'age',
align: 'center',
},
{
title: '年龄',
key: 'age',
align: 'center',
},
{
title: '工种',
key: 'age',
align: 'center',
},
])
//
const data = ref([
{
age: '2025-06-01 10:00:00',
tags: ['nice'],
},
{
age: '2025-06-01 10:00:00',
tags: ['nice'],
},
{
age: '2025-06-01 10:00:00',
tags: ['nice'],
},
{
age: '2025-06-01 10:00:00',
tags: ['nice'],
},
{
age: '2025-06-01 10:00:00',
tags: ['nice'],
},
{
age: '2025-06-01 10:00:00',
tags: ['nice'],
},
{
age: '2025-06-01 10:00:00',
tags: ['nice'],
},
{
age: '2025-06-01 10:00:00',
tags: ['nice'],
},
])
if (res.code == 200) {
outerUserList.value = res?.data
}
}
//
const checkIfShouldScroll = () => {
@ -406,18 +413,119 @@ const checkIfShouldScroll = () => {
}
}
//
onMounted(() => {
nextTick(checkIfShouldScroll)
const tableData_1 = ref([])
const tableTotal_1 = ref(0)
const tableColumns_1 = ref([
{
title: '序号',
key: 'index',
align: 'center',
width: 60,
render(row, index) {
return index + 1
},
},
{
title: '日期时间',
key: 'createTime',
align: 'center',
},
{
title: '动态图片',
key: 'dataImage',
align: 'center',
render(row) {
return h(NImage, {
width: '60px',
height: '60px',
src: imgPreviewUrl.value + row.dataImage,
fit: 'cover',
})
},
},
{
title: '人脸图片',
key: 'image',
align: 'center',
render(row) {
return h(NImage, {
width: '60px',
height: '60px',
src: imgPreviewUrl.value + row.image,
fit: 'cover',
})
},
},
{
title: '姓名',
key: 'userName',
align: 'center',
render(row) {
return row.userName || '-'
},
},
{
title: '年龄',
key: 'age',
align: 'center',
render(row) {
return row.age || '-'
},
},
{
title: '工种',
key: 'workType',
align: 'center',
render(row) {
return row.workType || '-'
},
},
])
const innerQueryParams_1 = ref({
pageNum: 1,
pageSize: 10,
startDay: '',
endDay: '',
userName: '',
workType: '',
})
onUpdated(() => {
nextTick(checkIfShouldScroll)
})
//
const getUserListPageFn = async () => {
const [startDay, endDay] = ranger_1?.value || []
innerQueryParams_1.value.startDay = startDay || ''
innerQueryParams_1.value.endDay = endDay || ''
const { data: res } = await getUserListPageApi(innerQueryParams_1.value)
if (res.code == 200) {
tableData_1.value = res?.rows
tableTotal_1.value = res?.total
}
}
//
const onHandleMore = () => {
morePanelVisible.value = true
const onHandleReset_1 = () => {
ranger_1.value = null
innerQueryParams_1.value = {
pageNum: 1,
pageSize: 10,
startDay: '',
endDay: '',
userName: '',
workType: '',
}
getUserListPageFn()
}
//
const onHandlePage_1Change = (page) => {
innerQueryParams_1.value.pageNum = page
getUserListPageFn()
}
//
const onHandlePageSize_1Change = (pageSize) => {
innerQueryParams_1.value.pageSize = pageSize
getUserListPageFn()
}
//
@ -430,21 +538,42 @@ const onHandleAddPerson = () => {
const onHandleSavePerson = () => {
addPersonFormRef.value.validate().then(async (valid) => {
if (valid) {
// formData
const formData = new FormData()
// formData
for (const key in addOrEditPersonForm.value) {
const value = addOrEditPersonForm.value[key]
if (value === undefined || value === null) continue
if (key === 'facePhoto') {
formData.append('file', addOrEditPersonForm.value.facePhoto[0].file)
if (Array.isArray(value) && value.length > 0) {
const photo = value[0]
if (!photo.id || photo.id !== 996) {
if (photo.file) {
formData.append('file', photo.file)
}
}
}
} else if (key === 'id' && value) {
formData.append('id', value)
} else {
formData.append(key, addOrEditPersonForm.value[key])
formData.append(key, value)
}
}
const { data: res } = await addPersonApi(formData)
const API = addOrEditPersonForm.value.id ? updatePersonApi : addPersonApi
const { data: res } = await API(formData)
if (res.code === 200) {
message.success('人员新增成功')
message.success(addOrEditPersonForm.value.id ? '人员编辑成功' : '人员新增成功')
addOrEditPersonForm.value.id = null
addOrEditPersonForm.value.facePhoto = []
previewFileList.value = []
addOrEditPersonForm.value.userName = ''
addOrEditPersonForm.value.mobile = ''
addOrEditPersonForm.value.idCard = ''
addOrEditPersonForm.value.age = ''
addOrEditPersonForm.value.sex = ''
addOrEditPersonForm.value.workType = ''
addPersonPanelVisible.value = false
getProjectPersonList()
} else {
message.error(res.message)
}
@ -452,6 +581,207 @@ const onHandleSavePerson = () => {
})
}
const innerQueryParams_2 = ref({
pageNum: 1,
pageSize: 10,
startDay: '',
endDay: '',
userName: '',
workType: '',
})
const btnList = ref([
{
label: '编辑',
type: 'info',
btnType: 1,
},
{
label: '删除',
type: 'info',
btnType: 2,
},
])
const tableData_2 = ref([])
const tableTotal_2 = ref(0)
const tableColumns_2 = ref([
{
title: '序号',
key: 'index',
align: 'center',
width: 60,
render(row, index) {
return index + 1
},
},
{
title: '日期时间',
key: 'createTime',
align: 'center',
},
{
title: '人脸照片',
key: 'image',
align: 'center',
render(row) {
return h(NImage, {
width: '60px',
height: '60px',
src: imgPreviewUrl.value + row.image,
fit: 'cover',
})
},
},
{
title: '姓名',
key: 'userName',
align: 'center',
render(row) {
return row.userName || '-'
},
},
{
title: '性别',
key: 'sex',
align: 'center',
render(row) {
return row.sex == 1 ? '男' : '女' || '-'
},
},
{
title: '年龄',
key: 'age',
align: 'center',
render(row) {
return row.age || '-'
},
},
{
title: '工种',
key: 'workType',
align: 'center',
render(row) {
return row.workType || '-'
},
},
{
title: '操作',
align: 'center',
//
render(row) {
const buttonS = btnList.value.map((btn) => {
return h(
NButton,
{
size: 'small',
type: btn.type,
style: {
marginRight: '4px',
},
onClick: () => onHandleBtn(row, btn.btnType),
},
{ default: () => btn.label },
)
})
return buttonS
},
},
])
//
const getProjectPersonList = async () => {
const [startDay, endDay] = ranger_2?.value || []
innerQueryParams_2.value.startDay = startDay || ''
innerQueryParams_2.value.endDay = endDay || ''
const { data: res } = await getUserInfoListApi(innerQueryParams_2.value)
if (res.code == 200) {
tableData_2.value = res?.rows
tableTotal_2.value = res?.total
}
}
//
const getPersonDetails = async (id) => {
const { data: res } = await getDetailsApi({ id })
if (res.code == 200) {
console.log(res.data)
}
}
//
const onHandleBtn = (row, btnType) => {
console.log(row, btnType)
if (btnType == 1) {
const { id, age, sex, workType, userName, mobile, image, idCard } = row
addOrEditPersonForm.value.age = age
addOrEditPersonForm.value.sex = sex
addOrEditPersonForm.value.workType = workType
addOrEditPersonForm.value.userName = userName
addOrEditPersonForm.value.mobile = mobile
addOrEditPersonForm.value.idCard = idCard
addOrEditPersonForm.value.id = id
addOrEditPersonForm.value.facePhoto = [
{ url: imgPreviewUrl.value + image, id: 996, name: '我很帅.png', status: 'finished' },
]
previewFileList.value = [
{
id: 996,
name: '我很帅.png',
status: 'finished',
url: imgPreviewUrl.value + image,
},
]
addPersonPanelVisible.value = true
} else if (btnType == 2) {
dialog.success({
title: '温馨提示',
content: '确定要删除该人员吗?',
positiveText: '确定',
negativeText: '取消',
draggable: true,
onPositiveClick: async () => {
const { data: res } = await deletePersonApi({
id: row.id,
})
if (res.code == 200) {
message.success('删除成功')
getProjectPersonList()
}
},
onNegativeClick: () => {},
})
}
}
const onHandleReset_2 = () => {
ranger_2.value = null
innerQueryParams_2.value = {
pageNum: 1,
pageSize: 10,
startDay: '',
endDay: '',
userName: '',
workType: '',
}
getProjectPersonList()
}
//
const onHandlePage_2Change = (page) => {
innerQueryParams_2.value.pageNum = page
getProjectPersonList()
}
//
const onHandlePageSize_2Change = (pageSize) => {
innerQueryParams_2.value.pageSize = pageSize
getProjectPersonList()
}
//
const showModal = ref(false)
const previewImageUrl = ref('')
@ -460,9 +790,43 @@ const handlePreview = (file) => {
showModal.value = true
}
const beforeUpload = (file) => {
const allowedTypes = ['image/jpeg', 'image/png']
if (!allowedTypes.includes(file.file.type)) {
message.error('仅支持 JPG/PNG/JPEG格式的图片')
return false
}
// 5MB
const maxSize = 5 * 1024 * 1024
if (file.file.size > maxSize) {
message.error('图片大小不能超过5MB')
return false
}
return true
}
const onHandleTabs = (index) => {
activeIndex.value = index
}
//
const onHandleMore = () => {
getUserListPageFn()
getProjectPersonList()
morePanelVisible.value = true
}
//
onMounted(() => {
nextTick(checkIfShouldScroll)
getUserListLimitFn()
})
onUpdated(() => {
nextTick(checkIfShouldScroll)
})
</script>
<style lang="scss" scoped>

View File

@ -8,15 +8,22 @@
<!-- 内容区域 -->
<div class="content-container">
<div class="content-item" v-for="item in 4" :key="item" :class="`item-${item}`">
<div
class="content-item"
:key="item.title"
v-for="(item, index) in compareList"
:class="`item-${index + 1}`"
>
<div>
<span style="padding-left: 10px; font-size: 16px"> 巡检次数 </span>
<span style="padding-left: 10px; font-size: 16px"> {{ item.title }} </span>
<span style="padding-left: 10px; font-size: 14px"> 总数 </span>
<span> 100 </span>
<span> {{ item.total }} </span>
</div>
<div>
<span style="padding-left: 10px; font-size: 18px; font-weight: bold"> 26 </span>
<span style="font-size: 14px"> </span>
<span style="padding-left: 10px; font-size: 18px; font-weight: bold">
{{ item.amount }}
</span>
<span style="font-size: 14px; margin-left: 4px"> {{ item.unit }} </span>
</div>
</div>
</div>
@ -68,6 +75,7 @@
<script setup>
import { NTag } from 'naive-ui'
import { ref, onMounted, onUpdated, nextTick, h } from 'vue'
import { getTaskStatisticsApi } from '@/api/home'
import TapsOne from './taps-one.vue'
import TapsTwo from './taps-two.vue'
import TapsThree from './taps-three.vue'
@ -79,6 +87,13 @@ const page = ref(1)
const pageSize = ref(10)
const activeIndex = ref(0)
const currentComponent = ref('comp-a')
const compareList = ref([
{ title: '巡检次数', total: 58, amount: 26, unit: '次' },
{ title: '巡检照片', total: 100, amount: 100, unit: '张' },
{ title: '比对次数', total: 100, amount: 100, unit: '次' },
{ title: '异常数量', total: 100, amount: 100, unit: '项' },
])
const componentMap = {
'comp-a': TapsOne,
'comp-b': TapsTwo,
@ -152,6 +167,26 @@ const data = ref([
},
])
//
const getTaskStatisticsData = async () => {
const { data: res } = await getTaskStatisticsApi()
console.log(res, 'res智能对比数据')
const { allErr, allImage, allSimilarity, allTask, dayErr, dayImage, daySimilarity, todayTask } =
res?.data
compareList.value[0].total = allTask
compareList.value[1].total = allImage
compareList.value[2].total = allSimilarity
compareList.value[3].total = allErr
compareList.value[0].amount = todayTask
compareList.value[1].amount = dayImage
compareList.value[2].amount = daySimilarity
compareList.value[3].amount = dayErr
}
getTaskStatisticsData()
const onHandleMore = () => {
console.log('更多')
morePanelVisible.value = true

View File

@ -20,18 +20,18 @@
<!-- 内容区域 -->
<n-data-table
:columns="columns"
:data="data"
flex-height
:columns="columns"
:scroll-x="10"
:style="{ height: `75%` }"
flex-height
/>
<div style="margin-top: 10px; display: flex; justify-content: flex-end">
<n-pagination
v-model:page="page"
v-model:page-size="pageSize"
:page-count="100"
show-size-picker
v-model:page="page"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
/>
</div>

View File

@ -65,8 +65,8 @@ export default defineConfig(({ mode }) => {
open: true,
proxy: {
'/robot-screen-api': {
// target: 'http://192.168.0.38:21999', // 郝志权测试环境
target: 'http://192.168.0.14:58080', // 测试环境
target: 'http://192.168.0.38:21999', // 郝志权测试环境
// target: 'http://192.168.0.14:58080', // 测试环境
changeOrigin: true,
rewrite: (p) => p.replace(/^\/robot-screen-api/, ''),
},