1.相机查询

2.取消流的日志
3.首页修改
4.相机控制
master
LAPTOP-S9HJSOEB\昊天 8 months ago
parent ea2fa52f8e
commit a0252bb5d5

@ -4,7 +4,7 @@ NODE_ENV=production
VITE_DEV=true VITE_DEV=true
# 请求路径 # 请求路径
VITE_BASE_URL='http://192.168.0.10:48080' VITE_BASE_URL='http://192.168.2.162:48080'
# 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务 # 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务
VITE_UPLOAD_TYPE=server VITE_UPLOAD_TYPE=server
@ -33,4 +33,4 @@ VITE_OUT_DIR=dist
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn' VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
# 验证码的开关 # 验证码的开关
VITE_APP_CAPTCHA_ENABLE=true VITE_APP_CAPTCHA_ENABLE=false

@ -4,7 +4,7 @@ NODE_ENV=production
VITE_DEV=false VITE_DEV=false
# 请求路径 # 请求路径
VITE_BASE_URL='http://192.168.0.10:48080' VITE_BASE_URL='http://192.168.0.162:48080'
# 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务 # 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务
VITE_UPLOAD_TYPE=server VITE_UPLOAD_TYPE=server
@ -31,3 +31,6 @@ VITE_OUT_DIR=dist-prod
# 商城H5会员端域名 # 商城H5会员端域名
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn' VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
# 验证码的开关
VITE_APP_CAPTCHA_ENABLE=false

@ -4,7 +4,7 @@ NODE_ENV=production
VITE_DEV=false VITE_DEV=false
# 请求路径 # 请求路径
VITE_BASE_URL='http://192.168.0.10:48080' VITE_BASE_URL='http://192.168.0.162:48080'
# 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务 # 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务
VITE_UPLOAD_TYPE=server VITE_UPLOAD_TYPE=server

@ -4,7 +4,7 @@ NODE_ENV=production
VITE_DEV=false VITE_DEV=false
# 请求路径 # 请求路径
VITE_BASE_URL='http://192.168.0.10:48080' VITE_BASE_URL='http://192.168.0.162:48080'
# 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务 # 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务
VITE_UPLOAD_TYPE=server VITE_UPLOAD_TYPE=server

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 KiB

@ -41,8 +41,8 @@ export const CameraApi = {
return request.get({ url: `/logistics/camera/list` }) return request.get({ url: `/logistics/camera/list` })
}, },
// 查询相机详情 // 查询相机详情
getCamera: async (id: number) => { getCamera: (id: number) => {
return await request.get({ url: `/logistics/camera/get?id=` + id }) return request.get({ url: `/logistics/camera/get?id=` + id })
}, },
// 新增相机 // 新增相机

@ -80,5 +80,10 @@ export const StockApi = {
//请求卡片信息接口 //请求卡片信息接口
getStockStatus: async (data: any) => { getStockStatus: async (data: any) => {
return await request.post({ url: `/logistics/stock/getStockStatus`, data }) return await request.post({ url: `/logistics/stock/getStockStatus`, data })
},
//请求卡片信息接口
artificial: async (data: any) => {
return await request.post({ url: `/logistics/stock/artificial`, data })
} }
} }

@ -18,6 +18,9 @@ export const getSimpleDictDataList = () => {
return request.get({ url: '/system/dict-data/simple-list' }) return request.get({ url: '/system/dict-data/simple-list' })
} }
export const getTypeList = (type: string) => {
return request.get({ url: '/system/dict-data/type?type='+type })
}
// 查询字典数据列表 // 查询字典数据列表
export const getDictDataPage = (params: PageParam) => { export const getDictDataPage = (params: PageParam) => {
return request.get({ url: '/system/dict-data/page', params }) return request.get({ url: '/system/dict-data/page', params })

@ -22,11 +22,12 @@ const props = defineProps({
type: Object as PropType<EChartsOption>, type: Object as PropType<EChartsOption>,
required: true required: true
}, },
dark: propTypes.bool.def(false),
width: propTypes.oneOfType([Number, String]).def(''), width: propTypes.oneOfType([Number, String]).def(''),
height: propTypes.oneOfType([Number, String]).def('500px') height: propTypes.oneOfType([Number, String]).def('500px')
}) })
const isDark = computed(() => appStore.getIsDark) const isDark = computed(() => props.dark|| appStore.getIsDark)
const theme = computed(() => { const theme = computed(() => {
const echartTheme: boolean | string = unref(isDark) ? true : 'auto' const echartTheme: boolean | string = unref(isDark) ? true : 'auto'
@ -58,7 +59,7 @@ const styles = computed(() => {
const initChart = () => { const initChart = () => {
if (unref(elRef) && props.options) { if (unref(elRef) && props.options) {
echartRef = echarts.init(unref(elRef) as HTMLElement) echartRef = echarts.init(unref(elRef) as HTMLElement, props.dark?'dark':'')
echartRef?.setOption(unref(options)) echartRef?.setOption(unref(options))
} }
} }

@ -46,7 +46,7 @@ export default {
setup(props) { setup(props) {
console.log(props.ptzShow) // console.log(props.ptzShow)
const video = ref(null) const video = ref(null)
const selfVideo = ref(null) const selfVideo = ref(null)
const url = ref(null) const url = ref(null)
@ -59,27 +59,37 @@ export default {
const msgrecv = ref(null) const msgrecv = ref(null)
// const streamUrl = ref(document.location.protocol + "//" + window.location.host + "/index/api/webrtc?app=live&stream=test&type=play"); // const streamUrl = ref(document.location.protocol + "//" + window.location.host + "/index/api/webrtc?app=live&stream=test&type=play");
const streamUrl = ref(
document.location.protocol +
'//' +
window.location.host +
':8096/index/api/webrtc?app=live&stream=camera' +
props.cameraId +
'&type=play'
)
const player = ref(null) const player = ref(null)
const recvOnly = ref(true) const recvOnly = ref(true)
const resolutions = ref([]) const resolutions = ref([])
const resolution = ref('') const resolution = ref('')
// cameraId // cameraId
watch(() => props.cameraId, (newCameraId, oldCameraId) => { watch(() => props.cameraId, (newCameraId, oldCameraId) => {
console.log(newCameraId); // console.log(newCameraId);
if (newCameraId !== oldCameraId) { if (newCameraId !== oldCameraId) {
ZLMRTCClient.GetAllScanResolution().forEach((r, i) => {
let text = r.label + '(' + r.width + 'x' + r.height + ')'
resolutions.value.push({
text: text,
value: r
})
})
resolution.value = resolutions.value[0].text
stopVideo()
startVideo()
// console.log(newCameraId);
// //
console.log(newCameraId+"54651561"); // console.log(newCameraId+"54651561");
} }
}), }),
onMounted(() => { onMounted(() => {
// formData.value = CameraApi.getCamera(props.cameraId)
// console.log(formData.value);
ZLMRTCClient.GetAllScanResolution().forEach((r, i) => { ZLMRTCClient.GetAllScanResolution().forEach((r, i) => {
let text = r.label + '(' + r.width + 'x' + r.height + ')' let text = r.label + '(' + r.width + 'x' + r.height + ')'
resolutions.value.push({ resolutions.value.push({
@ -89,32 +99,48 @@ export default {
}) })
resolution.value = resolutions.value[0].text resolution.value = resolutions.value[0].text
formData.value = CameraApi.getCamera(props.cameraId)
stopVideo() stopVideo()
startVideo() startVideo()
}) })
// //
onUnmounted(() => { onUnmounted(() => {
stopVideo() stopVideo()
}) })
const radioChange = (value) => { // const radioChange = (value) => {
let urlObj = new URL(streamUrl.value) // let urlObj = new URL(streamUrl.value)
urlObj.searchParams.set('type', value) // urlObj.searchParams.set('type', value)
streamUrl.value = urlObj.href // streamUrl.value = urlObj.href
if (value == 'play') { // if (value == 'play') {
recvOnly.value = true // recvOnly.value = true
} else if (value == 'echo') { // } else if (value == 'echo') {
recvOnly.value = false // recvOnly.value = false
} else { // } else {
recvOnly.value = false // recvOnly.value = false
} // }
} // }
const changeResolution = (e) => { const changeResolution = (e) => {
resolution.value = e.target.options[e.target.selectedIndex].text resolution.value = e.target.options[e.target.selectedIndex].text
} }
const start_play = () => { const start_play =async () => {
// const urlIp = ref("")
const resCamera = await CameraApi.getCamera(props.cameraId)
const urlIp = resCamera
console.log(urlIp);
const streamUrl = ref(
document.location.protocol +
'//' +
urlIp.rtcServer +
':'+urlIp.rtcServerPort+'/index/api/webrtc?app=live&stream=camera' +
props.cameraId +
'&type=play'
)
console.log(streamUrl.value);
let res = resolution.value.match(/\d+/g) let res = resolution.value.match(/\d+/g)
let h = parseInt(res.pop()) let h = parseInt(res.pop())
let w = parseInt(res.pop()) let w = parseInt(res.pop())
@ -137,15 +163,15 @@ export default {
}) })
player.value.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, function (e) { player.value.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, function (e) {
console.log('ICE 协商出错') // console.log('ICE ')
}) })
player.value.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, function (e) { player.value.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS, function (e) {
console.log('播放成功', e.streams) // console.log('', e.streams)
}) })
player.value.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, function (e) { player.value.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, function (e) {
console.log('offer anwser 交换失败', e) // console.log('offer anwser ', e)
stopVideo() stopVideo()
}) })
@ -155,42 +181,42 @@ export default {
}) })
player.value.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED, function (s) { player.value.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED, function (s) {
console.log('获取本地流失败') // console.log('')
}) })
player.value.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, function (state) { player.value.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, function (state) {
console.log('当前状态==>', state) // console.log('==>', state)
}) })
player.value.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_OPEN, function (event) { player.value.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_OPEN, function (event) {
console.log('rtc datachannel 打开 :', event) // // console.log('rtc datachannel :', event)
}) })
player.value.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_MSG, function (event) { player.value.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_MSG, function (event) {
console.log('rtc datachannel 消息 :', event.data) // console.log('rtc datachannel :', event.data)
msgrecv.value.value = event.data msgrecv.value.value = event.data
}) })
player.value.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_ERR, function (event) { player.value.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_ERR, function (event) {
console.log('rtc datachannel 错误 :', event) // console.log('rtc datachannel :', event)
}) })
player.value.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_CLOSE, function (event) { player.value.on(ZLMRTCClient.Events.WEBRTC_ON_DATA_CHANNEL_CLOSE, function (event) {
console.log('rtc datachannel 关闭 :', event) // console.log('rtc datachannel :', event)
}) })
} }
const startVideo = () => { const startVideo = () => {
console.log('开始') // console.log('')
stopVideo() stopVideo()
let res = resolution.value.match(/\d+/g) let res = resolution.value.match(/\d+/g)
let h = '50%' let h = '50%'
let w = '50%' let w = '50%'
console.log(h, w) // console.log(h, w)
console.log(video.value) // console.log(video.value)
start_play() start_play()
} }
const stopVideo = () => { const stopVideo = () => {
console.log('停止') // console.log('')
if (player.value) { if (player.value) {
player.value.close() player.value.close()
player.value = null player.value = null
@ -221,12 +247,12 @@ export default {
datachannel, datachannel,
msgsend, msgsend,
msgrecv, msgrecv,
streamUrl, // streamUrl,
player, player,
recvOnly, recvOnly,
resolutions, resolutions,
resolution, resolution,
radioChange, // radioChange,
changeResolution, changeResolution,
startVideo, startVideo,
stopVideo, stopVideo,

@ -105,6 +105,7 @@ export enum DICT_TYPE {
SHELVES_TYPE = 'shelves_type', SHELVES_TYPE = 'shelves_type',
LIGHT_TYPE = 'light_type', LIGHT_TYPE = 'light_type',
CAMERA_TYPE = 'camera_type', CAMERA_TYPE = 'camera_type',
CAMERA_CONF = 'camera_conf',
CHECK_STATUS = 'check_status', CHECK_STATUS = 'check_status',
ORDER_STATUS = 'order_status',//camera_type ORDER_STATUS = 'order_status',//camera_type
SIDE = 'side', SIDE = 'side',
@ -112,7 +113,8 @@ export enum DICT_TYPE {
TCP_CLIENT_STATUS = 'tcp_client_status',//tcp连接状态 TCP_CLIENT_STATUS = 'tcp_client_status',//tcp连接状态
TCP_PROCESSOR = 'tcp_processor',//tcp处理器类型 TCP_PROCESSOR = 'tcp_processor',//tcp处理器类型
TCP_LOG_STATUS= 'tcp_log_status',//tcp日志类型 SCANGUN_TYPE = 'scangun_type',
TCP_LOG_STATUS= 'tcp_log_status',//tcp日志类型
TERMINAL = 'terminal', // 终端 TERMINAL = 'terminal', // 终端
DATE_INTERVAL = 'date_interval', // 数据间隔 DATE_INTERVAL = 'date_interval', // 数据间隔

@ -1,43 +1,39 @@
<template> <template>
<ContentWrap> <ContentWrap class="body" >
<el-row :gutter="20"> <el-row :gutter="20">
<!-- 左侧区域 --> <!-- 左侧区域 -->
<el-col :span="7"> <el-col :span="7" >
<!-- 左上统计信息 --> <!-- 左上统计信息 -->
<el-card style="height: 250px" shadow="always" class="mb-20px"> <el-card class="box-card" style="height: 250px" shadow="always" >
<template #header> <template #header>
<div class="card-header"> <div class="card-header" :body-style="{ borderTop: 'none' }">
<span>统计信息</span> <span>统计信息</span>
</div> </div>
</template> </template>
<div> <div>
<p>总盘点数: {{ statistics.checkLogCount }}</p> <p class="colour-text">总盘点数: {{ statistics.checkLogCount }}</p>
<p>总随行数: {{ statistics.orderCount }}</p> <p class="colour-text">总随行数: {{ statistics.orderCount }}</p>
<p>月盘点数: {{ statistics.checkLogMonthCount }}</p> <p class="colour-text">月盘点数: {{ statistics.checkLogMonthCount }}</p>
<p>月随行数: {{ statistics.orderMonthCount }}</p> <p class="colour-text">月随行数: {{ statistics.orderMonthCount }}</p>
</div> </div>
</el-card> </el-card>
<!-- 左中条状图 --> <!-- 左中条状图 -->
<el-card shadow="always"> <el-card class="box-card" shadow="always">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>盘点状态</span> <span>盘点状态</span>
</div> </div>
</template> </template>
<Echart style="width: 100%; height: 200px" :options="deviceStatusChartOption" /> <Echart dark="true" style="width: 100%; height: 200px" :options="deviceStatusChartOption" />
</el-card> </el-card>
</el-col> </el-col>
<!-- 中间区域 --> <!-- 中间区域 -->
<el-col :span="10"> <el-col :span="10">
<!-- 中上和中中选择相机直播 --> <!-- 中上和中中选择相机直播 -->
<el-card shadow="always"> <el-card class="box-card" shadow="always" style="height: 590px">
<template #header>
<div class="card-header">
<span>选择相机</span>
</div>
</template>
<el-select v-model="selectedCamera" placeholder="请选择相机" style="width: 100%"> <el-select v-model="selectedCamera" placeholder="请选择相机" style="width: 100%">
<el-option <el-option
v-for="camera in cameraList" v-for="camera in cameraList"
@ -46,7 +42,7 @@
:value="camera.id" :value="camera.id"
/> />
</el-select> </el-select>
<div style="height: 420px; background-color: #555555" class="mt-20px"> <div style="height: 500px; background-color: #555555" class="mt-20px">
<Camera v-if="selectedCamera" :cameraId="selectedCamera" /> <Camera v-if="selectedCamera" :cameraId="selectedCamera" />
</div> </div>
</el-card> </el-card>
@ -55,7 +51,7 @@
<!-- 右侧区域 --> <!-- 右侧区域 -->
<el-col :span="7"> <el-col :span="7">
<!-- 右上快捷方式 --> <!-- 右上快捷方式 -->
<el-card style="height: 250px" shadow="always" class="mb-20px"> <el-card class="box-card" style="height: 250px" shadow="always">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>快捷方式</span> <span>快捷方式</span>
@ -69,10 +65,9 @@
<el-col v-for="item in shortcut" :key="`team-${item.name}`" :span="8" class="mb-50px"> <el-col v-for="item in shortcut" :key="`team-${item.name}`" :span="8" class="mb-50px">
<div class="flex items-center"> <div class="flex items-center">
<Icon :icon="item.icon" size="30" class="mr-3px" /> <Icon :icon="item.icon" size="30" class="mr-3px" />
<router-link :to="item.url"> <router-link :to="item.url">
<el-link type="default" :underline="false"> <el-link type="default" :underline="false">
{{ item.name }} <span class='white-text-icon'>{{ item.name }}</span>
</el-link> </el-link>
</router-link> </router-link>
</div> </div>
@ -81,13 +76,13 @@
</el-card> </el-card>
<!-- 右中另一个条状图 --> <!-- 右中另一个条状图 -->
<el-card shadow="always"> <el-card class="box-card" shadow="always">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>本月随行情况</span> <span>本月随行情况</span>
</div> </div>
</template> </template>
<Echart style="width: 100%; height: 200px" :options="orderChartOption" /> <Echart dark="true" style="width: 100%; height: 200px" :options="orderChartOption" />
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
@ -96,28 +91,28 @@
<el-row :gutter="20" class="mt-20px"> <el-row :gutter="20" class="mt-20px">
<el-col :span="7"> <el-col :span="7">
<!-- 左下实时滚动信息 --> <!-- 左下实时滚动信息 -->
<el-card shadow="always"> <el-card class="box-card" shadow="always">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>盘点饼状图</span> <span>盘点饼状图</span>
</div> </div>
</template> </template>
<Echart style="width: 100%; height: 200px" :options="stockPieOptions" /> <Echart dark="true" style="width: 100%; height: 200px" :options="stockPieOptions" />
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="10">
<el-card shadow="always"> <el-card class="box-card" shadow="always">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>随行折线图</span> <span>随行折线图</span>
</div> </div>
</template> </template>
<Echart style="width: 100%; height: 200px" :options="laneInventoryLineOptions" /> <Echart dark="true" style="width: 100%; height: 200px" :options="laneInventoryLineOptions" />
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="7"> <el-col :span="7">
<!-- 左下实时滚动信息 --> <!-- 左下实时滚动信息 -->
<el-card shadow="always"> <el-card class="box-card" shadow="always">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>实时滚动信息</span> <span>实时滚动信息</span>
@ -129,7 +124,10 @@
:key="index" :key="index"
class="log-item" class="log-item"
> >
<p class="colour-text">
{{ item }} {{ item }}
</p>
</div> </div>
</el-scrollbar> </el-scrollbar>
</el-card> </el-card>
@ -286,32 +284,32 @@ const getShortcut = async () => {
{ {
name: '随行记录', name: '随行记录',
icon: 'ep:data-analysis', icon: 'ep:data-analysis',
url: 'logistics/order' url: 'logistics/order',
}, },
{ {
name: '盘点管理', name: '盘点管理',
icon: 'ep:coin', icon: 'ep:coin',
url: 'logistics/check-log' url: 'logistics/check-log',
}, },
{ {
name: '盘点信息', name: '盘点信息',
icon: 'ep:pointer', icon: 'ep:pointer',
url: 'logistics/stock' url: 'logistics/stock',
}, },
{ {
name: '实时视频', name: '实时视频',
icon: 'ep:magic-stick', icon: 'ep:magic-stick',
url: 'cameraTree/cameraStreet' url: 'cameraTree/cameraStreet',
}, },
{ {
name: '巷道管理', name: '巷道管理',
icon: 'fa-solid:rainbow', icon: 'fa-solid:rainbow',
url: 'system/street' url: 'system/street',
}, },
{ {
name: '相机管理', name: '相机管理',
icon: 'ep:camera-filled', icon: 'ep:camera-filled',
url: 'system/camera' url: 'system/camera',
} }
] ]
shortcut = Object.assign(shortcut, data) shortcut = Object.assign(shortcut, data)
@ -337,13 +335,13 @@ let eventSource: any = null
const sseUid = ref(`${Date.now()}-${Math.floor(Math.random() * 10000)}`) const sseUid = ref(`${Date.now()}-${Math.floor(Math.random() * 10000)}`)
const sse = ()=>{ const sse = ()=>{
// EventSource SSE // EventSource SSE
eventSource = new EventSource('http://192.168.0.10:48080/app-api/sse/createSse?uid=' + sseUid.value ); eventSource = new EventSource(import.meta.env.VITE_BASE_URL+'/app-api/sse/createSse?uid=' + sseUid.value );
// //
eventSource.onmessage = (event) => { eventSource.onmessage = (event) => {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
console.log(data); console.log(data);
realTimeLogs.value.push(data.message)
}; };
// //
@ -467,12 +465,34 @@ onUnmounted(() => {
}) })
</script> </script>
<style scoped> <style scoped>
.body{
background-image:url(/public/pic02.jpg) ;
background-size:cover;
}
.card-header { .card-header {
display: flex; display: flex;
justify-content: space-between; justify-content: center;
align-items: center; align-items: center;
padding: 10px;
color: white;
background: url('/public/pic01.png') no-repeat center;
background-size: cover;
} }
.colour-text {
color: white;
}
.card-header::v-deep .el-card__header {
border-bottom: none !important;
}
.el-card {
border-color: rgba(5, 2, 53, 0.87); /* 深蓝色透明边框 */
background-color: rgba(5, 2, 53, 0.87); /* 深蓝色透明背景 */
}
.log-item { .log-item {
padding: 5px 0; padding: 5px 0;
border-bottom: 1px solid #ebeef5; border-bottom: 1px solid #ebeef5;
@ -487,6 +507,29 @@ onUnmounted(() => {
} }
.mb-20px { .mb-20px {
margin-bottom: 20px; margin-bottom: 30px;
}
.box-card {
margin-bottom: 20px;
}
.box-card ::v-deep .el-card__header {
border-bottom: none !important;
} }
.box-card ::v-deep .el-card__body {
background-color: #070225;
color: #7d6ea3ee; /* 接近黑色*/
}
.white-text-icon, .el-icon {
color: white !important;
}
/*.box-card { box-shadow: 0 0 10px rgba(0, 0, 255, 0.5); } */
.box-card { border-radius: 10px; } /* 加圆角*/
</style> </style>

@ -70,6 +70,7 @@ import * as authUtil from '@/utils/auth'
import { usePermissionStore } from '@/store/modules/permission' import { usePermissionStore } from '@/store/modules/permission'
import * as LoginApi from '@/api/login' import * as LoginApi from '@/api/login'
import { LoginStateEnum, useFormValid, useLoginState } from './useLogin' import { LoginStateEnum, useFormValid, useLoginState } from './useLogin'
import { count } from 'console'
defineOptions({ name: 'LoginForm' }) defineOptions({ name: 'LoginForm' })
@ -107,14 +108,15 @@ const loginData = reactive({
// //
const getCode = async () => { const getCode = async () => {
// await handleLogin({})
if (loginData.captchaEnable === 'false') { // //
await handleLogin({}) // if (loginData.captchaEnable === 'false') {
} else {
// // } else {
// // //
verify.value.show() // //
} // verify.value.show()
// }
} }
// //
const getLoginFormCache = () => { const getLoginFormCache = () => {
@ -160,12 +162,16 @@ const handleLogin = async (params) => {
// SSO // SSO
if (redirect.value.indexOf('sso') !== -1) { if (redirect.value.indexOf('sso') !== -1) {
console.log('SSO登录');
window.location.href = window.location.href.replace('/login?redirect=', '') window.location.href = window.location.href.replace('/login?redirect=', '')
} else { } else {
console.log(redirect.value); console.log('普通登录');
console.log(permissionStore.addRouters[0].path);
// console.log(redirect.value);
// console.log(permissionStore.addRouters[0].path);
push({ path: redirect.value || permissionStore.addRouters[0].path }) push({ path: redirect.value || permissionStore.addRouters[0].path })
// push({ path: redirect.value}) // push({ path: redirect.value})

@ -56,7 +56,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
import {getTypeList} from '@/api/system/dict/dict.data'
import { CameraApi, CameraVO } from '@/api/logistics/camera' import { CameraApi, CameraVO } from '@/api/logistics/camera'
import { number } from 'vue-types'
/** 相机 表单 */ /** 相机 表单 */
defineOptions({ name: 'CameraForm' }) defineOptions({ name: 'CameraForm' })
@ -68,7 +70,20 @@ const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // const dialogTitle = ref('') //
const formLoading = ref(false) // 12 const formLoading = ref(false) // 12
const formType = ref('') // create - update - const formType = ref('') // create - update -
const formData = ref({ const formData = ref<{
id?: number
name?: string
type?: number
ip?: string
port?: number
user?: string
password?: string
rtcServer?: string
rtcServerPort?: number
rtspPort?: number // number | undefined
channel?: string
recorderIp?: string
}>({
id: undefined, id: undefined,
name: undefined, name: undefined,
type: undefined, type: undefined,
@ -78,7 +93,7 @@ const formData = ref({
password: undefined, password: undefined,
rtcServer: undefined, rtcServer: undefined,
rtcServerPort: undefined, rtcServerPort: undefined,
rtspPort: undefined, rtspPort: undefined, // undefined number
channel: undefined, channel: undefined,
recorderIp: undefined, recorderIp: undefined,
}) })
@ -100,6 +115,29 @@ const open = async (type: string, id?: number) => {
} finally { } finally {
formLoading.value = false formLoading.value = false
} }
}else{
const cameraConf = await getTypeList(DICT_TYPE.CAMERA_CONF)
for(let i = 0; i < cameraConf.length; i++){
if(cameraConf[i].label == 'camera_password'){
formData.value.password = cameraConf[i].value
}else if(cameraConf[i].label == 'camera_type'){
formData.value.type = Number(cameraConf[i].value)
}else if(cameraConf[i].label == 'camera_user'){
formData.value.user = cameraConf[i].value
}else if(cameraConf[i].label == 'camera_port'){
formData.value.port = cameraConf[i].value
}else if(cameraConf[i].label == 'rtc_server_ip'){
formData.value.rtcServer = cameraConf[i].value
}else if(cameraConf[i].label == 'rtc_server_port'){
formData.value.rtcServerPort = cameraConf[i].value
}else if(cameraConf[i].label == 'rtsp_port'){
formData.value.rtspPort = cameraConf[i].value
}else if(cameraConf[i].label == 'camera_cannel'){
formData.value.channel = cameraConf[i].value
}
}
console.log(cameraConf);
} }
} }
defineExpose({ open }) // open defineExpose({ open }) // open

@ -108,6 +108,17 @@ const addRouters = (id: number) => {
getList() getList()
} }
// cameraId
watch(
() => props.cameraId,
(newVal, oldVal) => {
if (newVal !== oldVal) {
formData.value.cameraId = newVal // formData cameraId
getList() //
}
}
)
// //
const showModelDel = async (id: number) => { const showModelDel = async (id: number) => {
try { try {
@ -131,6 +142,9 @@ onMounted(() => {
onUnmounted(() => { onUnmounted(() => {
// //
console.log('组件销毁时执行清理逻辑');
list.value = [] //
}) })
</script> </script>

@ -11,7 +11,7 @@
highlight-current highlight-current
class="camera-tree" class="camera-tree"
/> />
<ptz :cameraId="cameraId" /> <ptz v-if="cameraId" :cameraId="cameraId" />
</el-col> </el-col>
<el-col :span="19"> <el-col :span="19">
<div class="container"> <div class="container">
@ -38,8 +38,9 @@
@click="clickCameraFun((rowIndex-1)*topNum +colIndex)" @click="clickCameraFun((rowIndex-1)*topNum +colIndex)"
> >
<div v-if="((rowIndex-1)*topNum +colIndex)==cameraIndex" class="floating-text"><Icon :size="15" icon="ep:circle-close" <div v-if="((rowIndex-1)*topNum +colIndex)==cameraIndex" class="floating-text">
/></div> <Icon :size="15" icon="ep:circle-close" @click="deleteCameraFun((rowIndex-1)*topNum +colIndex)"/>
</div>
<Camera v-if="getCameraId((rowIndex - 1) * topNum + colIndex)!== 0" <Camera v-if="getCameraId((rowIndex - 1) * topNum + colIndex)!== 0"
:cameraId="getCameraId((rowIndex-1)*topNum +colIndex)" :ptzShow="false" /> :cameraId="getCameraId((rowIndex-1)*topNum +colIndex)" :ptzShow="false" />
@ -80,6 +81,15 @@ const getTree = async () => {
} }
} }
const selectedNodeId = ref<string | null>(null) const selectedNodeId = ref<string | null>(null)
//
const deleteCameraFun = (index)=>{
console.log("删除摄像头 index:",index );
clickCamera.value = true
CameraMap.value?.delete(index)
cameraIndex.value = index
cameraId.value = null
}
// //
const clickCameraFun = (index)=>{ const clickCameraFun = (index)=>{
@ -97,6 +107,7 @@ const handleNodeClick = (data: any) => {
if (data.id != null) { if (data.id != null) {
// //
if(clickCamera.value){ if(clickCamera.value){
deleteCameraFun(cameraIndex.value)
CameraMap.value?.set(cameraIndex.value, data.id) CameraMap.value?.set(cameraIndex.value, data.id)
}else{ }else{
// cameraId // cameraId

@ -193,6 +193,16 @@
<el-table-column label="操作" align="center"> <el-table-column label="操作" align="center">
<template #default="scope"> <template #default="scope">
<el-button
link
type="success"
@click="getDialogVisible(scope.row.streetName,scope.row.direction,scope.row.row, scope.row.column,scope.row.id)"
v-hasPermi="['logistics:check-log:success']"
>
操作
</el-button>
<el-button <el-button
link link
type="danger" type="danger"

@ -139,10 +139,11 @@
<space >{{ scope.row.srmNumber+"-"+ (scope.row.toDirection?"左侧-":"右侧-")+ scope.row.toRow+'层-'+scope.row.toColumn+'列' }}</space> <space >{{ scope.row.srmNumber+"-"+ (scope.row.toDirection?"左侧-":"右侧-")+ scope.row.toRow+'层-'+scope.row.toColumn+'列' }}</space>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="图片" align="center" prop="pics" >
<el-table-column label="图片" align="center" prop="pics" min-width="150">
<template #default="scope"> <template #default="scope">
<div v-if="scope.row.pics"> <div v-if="scope.row.pics" class="image-cell-container">
<el-image <el-image
v-for="(pic, index) in scope.row.pics.split(';')" v-for="(pic, index) in scope.row.pics.split(';')"
:key="index" :key="index"
@ -152,6 +153,7 @@
:initial-index="0" :initial-index="0"
:preview-teleported="true" :preview-teleported="true"
fit="cover" fit="cover"
:disabled="true"
/> />
</div> </div>
<div v-else> <div v-else>
@ -162,10 +164,11 @@
<el-table-column label="操作" align="center"> <el-table-column label="操作" align="center">
<template #default="scope"> <template #default="scope">
<el-button <el-button
v-if="scope.row.videoPath1!=null" v-if="scope.row.videoPath1 && scope.row.videoPath1.trim() !== '' &&
scope.row.videoPath2 && scope.row.videoPath2.trim() !== ''"
link link
type="primary" type="primary"
@click="videoPlay(scope.row.videoPath2,scope.row.videoPath2)" @click="videoPlay(scope.row.videoPath1,scope.row.videoPath2)"
v-hasPermi="['logistics:order:delete']" v-hasPermi="['logistics:order:delete']"
> >
视频 视频
@ -190,28 +193,17 @@
@pagination="getList" @pagination="getList"
/> />
<el-dialog <el-dialog v-model="dialogVisible" title="视频" width="500">
v-model="dialogVisible" <video ref="videoPlayer1" width="100%" controls autoplay muted>
title="视频" <source :src="video1Src" type="video/mp4" />
width="500" 您的浏览器不支持 video 标签
> </video>
<video ref="videoPlayer" width="100%" controls autoplay muted>
<source :src="video1Src" type="video/mp4" />
您的浏览器不支持 video 标签
</video>
<video ref="videoPlayer" width="100%" controls autoplay muted> <video ref="videoPlayer2" width="100%" controls autoplay muted>
<source :src="video2Src" type="video/mp4" /> <source :src="video2Src" type="video/mp4" />
您的浏览器不支持 video 标签 您的浏览器不支持 video 标签
</video> </video>
<!-- </el-dialog>
<video muted autoplay="true" >
<source
:src="video2Src.value"
type="video/mp4"
>
</video> -->
</el-dialog>
</ContentWrap> </ContentWrap>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
@ -266,13 +258,29 @@ const exportLoading = ref(false) // 导出的加载中
const video1Src = ref("") // const video1Src = ref("") //
const video2Src = ref("") // const video2Src = ref("") //
const videoPlay = ((video1,video2) =>{ const videoPlayer1 = ref<HTMLVideoElement | null>(null)
console.log(video1,video2); const videoPlayer2 = ref<HTMLVideoElement | null>(null)
const videoPlay = (video1: string, video2: string) => {
//
dialogVisible.value = false
nextTick(() => {
dialogVisible.value = true
dialogVisible.value = true //
video1Src.value = video1 video1Src.value = video1
video2Src.value = video2 video2Src.value = video2
})
// DOM load()
nextTick(() => {
if (videoPlayer1.value) {
videoPlayer1.value.load()
}
if (videoPlayer2.value) {
videoPlayer2.value.load()
}
})
})
}
/** 查询列表 */ /** 查询列表 */
const getList = async () => { const getList = async () => {
loading.value = true loading.value = true
@ -355,3 +363,19 @@ onMounted(() => {
getStringList() getStringList()
}) })
</script> </script>
<style scoped>
.image-cell-container {
display: flex;
overflow-x: auto;
white-space: nowrap;
}
.image-cell-container::-webkit-scrollbar {
height: 6px;
}
.image-cell-container::-webkit-scrollbar-thumb {
background-color: #ccc;
border-radius: 3px;
}
</style>

@ -14,6 +14,7 @@
placeholder="请选择巷道" placeholder="请选择巷道"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
@change="handleQuery"
class="!w-240px" class="!w-240px"
> >
<el-option <el-option
@ -29,6 +30,7 @@
v-model="queryParams.direction" v-model="queryParams.direction"
clearable clearable
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
@change="handleQuery"
class="!w-240px" class="!w-240px"
> >
<el-option <el-option
@ -40,7 +42,14 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item ><el-button @click="handleQuery" type="primary">搜索</el-button></el-form-item> <!-- <el-form-item ><el-button @click="handleQuery" type="primary">搜索</el-button></el-form-item> -->
<el-form-item >
<el-button
v-for="(button, index) in colorList"
:type="colorList[index]"
:key="index">{{statusList[index]}}</el-button>
</el-form-item>
</el-form> </el-form>
</ContentWrap> </ContentWrap>
@ -144,6 +153,9 @@ import Cookies from 'js-cookie';
const message = useMessage() // const message = useMessage() //
const { t } = useI18n() // ist const { t } = useI18n() // ist
const statusList = ['未盘点', '盘点异常', '盘点正确', '人工盘点正常'];
const colorList = ['warning', 'danger', 'success', 'success'];
const colorListHex = {'未盘点':'#ebc26e','盘点异常':'#ed6d6d','盘点正确':'#6cc238','人工盘点正常':'#b1c238'};
let rows = ref(12); let rows = ref(12);
let columns = ref(48); let columns = ref(48);
// //
@ -230,12 +242,20 @@ const saveQueryParamsToCookie = () => {
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = async () => { const handleQuery = async () => {
columnMap.value = [] columnMap.value = []
const data = await StockApi.getStreetList(queryParams) if(queryParams.streetId == null ||queryParams.streetId ==undefined){
saveQueryParamsToCookie() queryParams.streetId = 0
getStreetStatus() }
if(queryParams.direction == null ||queryParams.direction ==undefined){
queryParams.direction = 0
}
const data = await StockApi.getStreetList(queryParams)
buttons.value = data.buttons buttons.value = data.buttons
rows = ref(data.rows) rows = ref(data.rows)
columns =ref(data.columns) columns =ref(data.columns)
saveQueryParamsToCookie()
getStreetStatus()
const page = columns.value/columnMax.value const page = columns.value/columnMax.value
for(var i = 0; i <= page; i++){ for(var i = 0; i <= page; i++){
@ -376,7 +396,14 @@ const option = reactive({
itemStyle: { itemStyle: {
borderRadius: 10, borderRadius: 10,
borderColor: '#fff', borderColor: '#fff',
borderWidth: 2 borderWidth: 2,
color: function (params) {
console.log(params.name);
// var status = buttonByStatusMap.value.get(params.name);
// 使 params.dataIndex
return colorListHex[params.name];
}
}, },
label: { label: {
show: false, show: false,
@ -392,13 +419,12 @@ const option = reactive({
labelLine: { labelLine: {
show: false show: false
}, // }, //
normal: { // normal: {
color: function(params) { // color: function(params) {
// params.name // // params.name
const colorList = ['#87cefa', '#da70d6', '#32cd32', '#ff6347']; // return colorList[params.dataIndex]; // index
return colorList[params.dataIndex]; // index // }
} // },
},
data: echartData() data: echartData()
} }
] ]

@ -34,6 +34,9 @@
</div> </div>
</div> </div>
</div> </div>
<div class="mt-4">
<el-button type="success" size="20" style="width: 50%;" @click="artificial()" >人工盘点</el-button>
</div>
</div> </div>
</div> </div>
<!-- </div> <!-- </div>
@ -43,7 +46,7 @@
<script> <script>
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { StockApi } from '@/api/logistics/stock'; import { StockApi } from '@/api/logistics/stock';
import { ElMessage } from 'element-plus'
export default { export default {
props: { props: {
stockId: { stockId: {
@ -63,8 +66,9 @@ export default {
const stockInfo = ref([]); const stockInfo = ref([]);
const showEnlarged = ref(false); const showEnlarged = ref(false);
const enlargedImageUrl = ref(''); const enlargedImageUrl = ref('');
const resStatus = ref(true);
// //
const fetchStockInfo = async () => { const fetchStockInfo = async () => {
try { try {
const response = await StockApi.getStockStatus({ const response = await StockApi.getStockStatus({
@ -72,6 +76,8 @@ export default {
checkLogId: props.checkLogId checkLogId: props.checkLogId
}); });
let statusString = "盘点正确"; let statusString = "盘点正确";
resStatus.value = response.status ==="2" || response.status ==="3" || response.status ===2 || response.status ===3;
console.log(resStatus);
const scanData = response.scan || []; const scanData = response.scan || [];
// //
// const hasInconsistent = scanData.some(item => // const hasInconsistent = scanData.some(item =>
@ -147,6 +153,14 @@ export default {
enlargedImageUrl.value = url; enlargedImageUrl.value = url;
showEnlarged.value = true; showEnlarged.value = true;
}; };
const artificial = async () => {
const response = await StockApi.artificial({
stockId: props.stockId,
checkLogId: props.checkLogId
});
resStatus.value = true;
ElMessage.status('人工识别成功')
};
const closeBoxCard = () => { const closeBoxCard = () => {
emit('close-box-card'); emit('close-box-card');
@ -158,6 +172,7 @@ export default {
stockInfo, stockInfo,
showEnlarged, showEnlarged,
enlargedImageUrl, enlargedImageUrl,
artificial,
showEnlargedImage, showEnlargedImage,
closeBoxCard closeBoxCard
}; };

@ -23,6 +23,21 @@
</el-form-item> </el-form-item>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="类型" min-width="150">
<template #default="{ row, $index }">
<el-form-item :prop="`${$index}.type`" :rules="formRules.type" class="mb-0px!">
<el-select v-model="row.type" placeholder="请选择类型">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.SCANGUN_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="ip" min-width="150"> <el-table-column label="ip" min-width="150">
<template #default="{ row, $index }"> <template #default="{ row, $index }">
<el-form-item :prop="`${$index}.ip`" :rules="formRules.ip" class="mb-0px!"> <el-form-item :prop="`${$index}.ip`" :rules="formRules.ip" class="mb-0px!">
@ -60,7 +75,6 @@ const formData = ref([])
const formRules = reactive({ const formRules = reactive({
}) })
const formRef = ref() // Ref const formRef = ref() // Ref
/** 监听主表的关联字段的变化,加载对应的子表数据 */ /** 监听主表的关联字段的变化,加载对应的子表数据 */
watch( watch(
() => props.streetId, () => props.streetId,
@ -87,6 +101,7 @@ const handleAdd = () => {
id: undefined, id: undefined,
streetId: undefined, streetId: undefined,
direction: undefined, direction: undefined,
type: undefined,
ip: undefined, ip: undefined,
port: undefined, port: undefined,
} }

Loading…
Cancel
Save