feat: 调整登录界面

This commit is contained in:
zhanghao 2026-08-03 16:52:27 +08:00
parent 10a62c51d2
commit 0c52f8b909
7 changed files with 161 additions and 62 deletions

3
.env
View File

@ -7,3 +7,6 @@ VITE_SERVICE_PORT=5175
# /home/cmvr/zh/mkcert 目录中的证书文件名 # /home/cmvr/zh/mkcert 目录中的证书文件名
HTTPS_CERT_FILENAME=192.168.0.224+2.pem HTTPS_CERT_FILENAME=192.168.0.224+2.pem
HTTPS_KEY_FILENAME=192.168.0.224+2-key.pem HTTPS_KEY_FILENAME=192.168.0.224+2-key.pem
VITE_VIDEO_SERVER_HOST=http://192.168.0.224
VITE_TERMINAL_ID=c2f8a06826baf03843925c1a2a13bcfd

2
components.d.ts vendored
View File

@ -20,7 +20,9 @@ declare module 'vue' {
ElIcon: typeof import('element-plus/es')['ElIcon'] ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput'] ElInput: typeof import('element-plus/es')['ElInput']
ElMain: typeof import('element-plus/es')['ElMain'] ElMain: typeof import('element-plus/es')['ElMain']
ElOption: typeof import('element-plus/es')['ElOption']
ElRow: typeof import('element-plus/es')['ElRow'] ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSlider: typeof import('element-plus/es')['ElSlider'] ElSlider: typeof import('element-plus/es')['ElSlider']
ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTag: typeof import('element-plus/es')['ElTag'] ElTag: typeof import('element-plus/es')['ElTag']

View File

@ -226,7 +226,7 @@ app.post('/api/agv/getCurrentMap', async (req, res) => {
// 机器人运动控制 // 机器人运动控制
app.post('/api/agv/moveRobot', async (req, res) => { app.post('/api/agv/moveRobot', async (req, res) => {
const grpcClient = setVelocity(req.body.ip) const grpcClient = createAGVGrpcClient(req.body.ip)
const request = { const request = {
header: { header: {
device_id: req.body.deviceId || 'agv_src1100' device_id: req.body.deviceId || 'agv_src1100'

View File

@ -4,6 +4,7 @@ import { defineStore } from 'pinia'
export const useRobotStore = defineStore('robot', { export const useRobotStore = defineStore('robot', {
state: () => ({ state: () => ({
ip: '192.168.0.28:50052', ip: '192.168.0.28:50052',
robotType: 'inspection',
agvDeviceId:'src1100', agvDeviceId:'src1100',
almDeviceId: 'huayan_arm', almDeviceId: 'huayan_arm',
spkDeviceId: 'spk1', spkDeviceId: 'spk1',
@ -32,6 +33,9 @@ export const useRobotStore = defineStore('robot', {
setIp(str) { setIp(str) {
this.ip = str this.ip = str
}, },
setRobotType(str) {
this.robotType = str
},
setAgvDeviceId(str) { setAgvDeviceId(str) {
this.agvDeviceId = str this.agvDeviceId = str
}, },

View File

@ -9,7 +9,13 @@
<el-form-item label="IP和端口" prop="ip"> <el-form-item label="IP和端口" prop="ip">
<el-input v-model="ruleForm.ip" placeholder="请输入IP和端口" /> <el-input v-model="ruleForm.ip" placeholder="请输入IP和端口" />
</el-form-item> </el-form-item>
<el-form-item label="底盘设备ID" prop="agvDeviceId"> <el-form-item label="机器人类型" prop="robotType">
<el-select v-model="ruleForm.robotType" placeholder="请选择机器人类型">
<el-option label="巡检机器人" value="inspection"></el-option>
<el-option label="讲解机器人" value="explanation"></el-option>
</el-select>
</el-form-item>
<!-- <el-form-item label="底盘设备ID" prop="agvDeviceId">
<el-input v-model="ruleForm.agvDeviceId" placeholder="请输入底盘设备ID" /> <el-input v-model="ruleForm.agvDeviceId" placeholder="请输入底盘设备ID" />
</el-form-item> </el-form-item>
<el-form-item label="机械臂设备ID" prop="almDeviceId"> <el-form-item label="机械臂设备ID" prop="almDeviceId">
@ -23,7 +29,7 @@
</el-form-item> </el-form-item>
<el-form-item label="相机设备ID" prop="cameraDeviceId"> <el-form-item label="相机设备ID" prop="cameraDeviceId">
<el-input v-model="ruleForm.cameraDeviceId" placeholder="请输入相机设备ID" /> <el-input v-model="ruleForm.cameraDeviceId" placeholder="请输入相机设备ID" />
</el-form-item> </el-form-item> -->
</el-form> </el-form>
<el-button :loading="loading" @click="testConnect" class="connect">进入上位机控制页面</el-button> <el-button :loading="loading" @click="testConnect" class="connect">进入上位机控制页面</el-button>
</div> </div>
@ -46,32 +52,36 @@ const loading = ref(false)
const ruleFormRef = ref() const ruleFormRef = ref()
const ruleForm = ref({ const ruleForm = ref({
ip: '', ip: '',
agvDeviceId:'src1100', robotType: 'inspection',
almDeviceId: 'huayan_arm', // agvDeviceId:'src1100',
spkDeviceId: 'spk1', // almDeviceId: 'huayan_arm',
micDeviceId: 'mic1', // spkDeviceId: 'spk1',
cameraDeviceId: 'real_cam1' // micDeviceId: 'mic1',
// cameraDeviceId: 'real_cam1'
}) })
const rules = ref({ const rules = ref({
ip: [ ip: [
{ required: true, message: '请输入机器人IP和端口', trigger: 'blur' } { required: true, message: '请输入机器人IP和端口', trigger: 'blur' }
], ],
agvDeviceId: [ robotType: [
{ required: true, message: '请输入底盘设备ID', trigger: 'blur' } { required: true, message: '请选择机器人类型', trigger: 'blur' }
],
almDeviceId: [
{ required: true, message: '请输入机械臂设备ID', trigger: 'blur' }
],
spkDeviceId: [
{ required: true, message: '请输入扬声器设备ID', trigger: 'blur' }
],
micDeviceId: [
{ required: true, message: '请输入麦克风设备ID', trigger: 'blur' }
],
cameraDeviceId: [
{ required: true, message: '请输入相机设备ID', trigger: 'blur' }
], ],
// agvDeviceId: [
// { required: true, message: 'ID', trigger: 'blur' }
// ],
// almDeviceId: [
// { required: true, message: 'ID', trigger: 'blur' }
// ],
// spkDeviceId: [
// { required: true, message: 'ID', trigger: 'blur' }
// ],
// micDeviceId: [
// { required: true, message: 'ID', trigger: 'blur' }
// ],
// cameraDeviceId: [
// { required: true, message: 'ID', trigger: 'blur' }
// ],
}) })
const testConnect = async () => { const testConnect = async () => {
@ -79,16 +89,26 @@ const testConnect = async () => {
if (valid) { if (valid) {
loading.value = true loading.value = true
robotStore.setIp(ruleForm.value.ip) robotStore.setIp(ruleForm.value.ip)
robotStore.setAgvDeviceId(ruleForm.value.agvDeviceId) robotStore.setRobotType(ruleForm.value.robotType)
robotStore.setAlmDeviceId(ruleForm.value.almDeviceId) if (ruleForm.value.robotType === 'inspection') {
robotStore.setSpkDeviceId(ruleForm.value.spkDeviceId) robotStore.setAgvDeviceId('src1100')
robotStore.setMicDeviceId(ruleForm.value.micDeviceId) robotStore.setAlmDeviceId('huayan_arm')
robotStore.setCameraDeviceId(ruleForm.value.cameraDeviceId) robotStore.setSpkDeviceId('spk1')
robotStore.setMicDeviceId('mic1')
robotStore.setCameraDeviceId('real_cam1')
} else if (ruleForm.value.robotType === 'explanation') {
robotStore.setAgvDeviceId('src1100')
robotStore.setAlmDeviceId('huayan_arm')
robotStore.setSpkDeviceId('spk1')
robotStore.setMicDeviceId('mic1')
robotStore.setCameraDeviceId('real_cam1')
}
// const result = await getRuntimeState({ // const result = await getRuntimeState({
// ip: robotStore.ip, // ip: robotStore.ip,
// deviceId: ruleForm.value.agvDeviceId // deviceId: ruleForm.value.agvDeviceId
// }) // })
// loading.value = false loading.value = false
// if (result.code === 200) { // if (result.code === 200) {
// robotStore.setPosition(result.data.pose) // robotStore.setPosition(result.data.pose)
// robotStore.setBattery(result.data.battery) // robotStore.setBattery(result.data.battery)
@ -150,12 +170,23 @@ const testConnect = async () => {
margin-bottom: 12px; margin-bottom: 12px;
} }
:deep(.el-form-item__label) {
font-size: 16px;
line-height: 40px;
}
:deep(.el-input__inner) { :deep(.el-input__inner) {
height: 40px; height: 40px;
// iPad Safari // iPad Safari
font-size: 16px; font-size: 16px;
} }
:deep(.el-select__wrapper) {
height: 40px;
// iPad Safari
font-size: 16px;
}
.connect { .connect {
width: 340px; width: 340px;
height: 50px; height: 50px;

View File

@ -97,16 +97,16 @@
<div class="axis-label x">X</div> <div class="axis-label x">X</div>
<div class="axis-slider-wrap"> <div class="axis-slider-wrap">
<div class="axis-name">前后平移</div> <div class="axis-name">前后平移</div>
<div> <div class="axis-control-buttons">
<el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('x', '-', 'end'))" <el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('x', '-', 'end'))"
@pointerup="endPointerControl" @pointercancel="endPointerControl"> @pointerup="endPointerControl" @pointercancel="endPointerControl">
<el-icon size="24"> <el-icon size="32">
<Remove /> <Remove />
</el-icon> </el-icon>
</el-button> </el-button>
<el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('x', '+', 'end'))" <el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('x', '+', 'end'))"
@pointerup="endPointerControl" @pointercancel="endPointerControl"> @pointerup="endPointerControl" @pointercancel="endPointerControl">
<el-icon size="24"> <el-icon size="32">
<CirclePlus /> <CirclePlus />
</el-icon> </el-icon>
</el-button> </el-button>
@ -119,16 +119,16 @@
<div class="axis-label y">Y</div> <div class="axis-label y">Y</div>
<div class="axis-slider-wrap"> <div class="axis-slider-wrap">
<div class="axis-name">左右平移</div> <div class="axis-name">左右平移</div>
<div> <div class="axis-control-buttons">
<el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('y', '-', 'end'))" <el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('y', '-', 'end'))"
@pointerup="endPointerControl" @pointercancel="endPointerControl"> @pointerup="endPointerControl" @pointercancel="endPointerControl">
<el-icon size="24"> <el-icon size="32">
<Remove /> <Remove />
</el-icon> </el-icon>
</el-button> </el-button>
<el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('y', '+', 'end'))" <el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('y', '+', 'end'))"
@pointerup="endPointerControl" @pointercancel="endPointerControl"> @pointerup="endPointerControl" @pointercancel="endPointerControl">
<el-icon size="24"> <el-icon size="32">
<CirclePlus /> <CirclePlus />
</el-icon> </el-icon>
</el-button> </el-button>
@ -141,16 +141,16 @@
<div class="axis-label z">Z</div> <div class="axis-label z">Z</div>
<div class="axis-slider-wrap"> <div class="axis-slider-wrap">
<div class="axis-name">上下平移</div> <div class="axis-name">上下平移</div>
<div> <div class="axis-control-buttons">
<el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('z', '-', 'end'))" <el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('z', '-', 'end'))"
@pointerup="endPointerControl" @pointercancel="endPointerControl"> @pointerup="endPointerControl" @pointercancel="endPointerControl">
<el-icon size="24"> <el-icon size="32">
<Remove /> <Remove />
</el-icon> </el-icon>
</el-button> </el-button>
<el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('z', '+', 'end'))" <el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('z', '+', 'end'))"
@pointerup="endPointerControl" @pointercancel="endPointerControl"> @pointerup="endPointerControl" @pointercancel="endPointerControl">
<el-icon size="24"> <el-icon size="32">
<CirclePlus /> <CirclePlus />
</el-icon> </el-icon>
</el-button> </el-button>
@ -177,16 +177,16 @@
<div class="axis-label x">X</div> <div class="axis-label x">X</div>
<div class="axis-slider-wrap"> <div class="axis-slider-wrap">
<div class="axis-name">前后平移</div> <div class="axis-name">前后平移</div>
<div> <div class="axis-control-buttons">
<el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('x', '-', 'posture'))" <el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('x', '-', 'posture'))"
@pointerup="endPointerControl" @pointercancel="endPointerControl"> @pointerup="endPointerControl" @pointercancel="endPointerControl">
<el-icon size="24"> <el-icon size="32">
<Remove /> <Remove />
</el-icon> </el-icon>
</el-button> </el-button>
<el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('x', '+', 'posture'))" <el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('x', '+', 'posture'))"
@pointerup="endPointerControl" @pointercancel="endPointerControl"> @pointerup="endPointerControl" @pointercancel="endPointerControl">
<el-icon size="24"> <el-icon size="32">
<CirclePlus /> <CirclePlus />
</el-icon> </el-icon>
</el-button> </el-button>
@ -199,16 +199,16 @@
<div class="axis-label y">Y</div> <div class="axis-label y">Y</div>
<div class="axis-slider-wrap"> <div class="axis-slider-wrap">
<div class="axis-name">左右平移</div> <div class="axis-name">左右平移</div>
<div> <div class="axis-control-buttons">
<el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('y', '-', 'posture'))" <el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('y', '-', 'posture'))"
@pointerup="endPointerControl" @pointercancel="endPointerControl"> @pointerup="endPointerControl" @pointercancel="endPointerControl">
<el-icon size="24"> <el-icon size="32">
<Remove /> <Remove />
</el-icon> </el-icon>
</el-button> </el-button>
<el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('y', '+', 'posture'))" <el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('y', '+', 'posture'))"
@pointerup="endPointerControl" @pointercancel="endPointerControl"> @pointerup="endPointerControl" @pointercancel="endPointerControl">
<el-icon size="24"> <el-icon size="32">
<CirclePlus /> <CirclePlus />
</el-icon> </el-icon>
</el-button> </el-button>
@ -221,16 +221,16 @@
<div class="axis-label z">Z</div> <div class="axis-label z">Z</div>
<div class="axis-slider-wrap"> <div class="axis-slider-wrap">
<div class="axis-name">上下平移</div> <div class="axis-name">上下平移</div>
<div> <div class="axis-control-buttons">
<el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('z', '-', 'posture'))" <el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('z', '-', 'posture'))"
@pointerup="endPointerControl" @pointercancel="endPointerControl"> @pointerup="endPointerControl" @pointercancel="endPointerControl">
<el-icon size="24"> <el-icon size="32">
<Remove /> <Remove />
</el-icon> </el-icon>
</el-button> </el-button>
<el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('z', '+', 'posture'))" <el-button class="press-control" type="primary" circle @pointerdown="startPointerControl($event, () => move('z', '+', 'posture'))"
@pointerup="endPointerControl" @pointercancel="endPointerControl"> @pointerup="endPointerControl" @pointercancel="endPointerControl">
<el-icon size="24"> <el-icon size="32">
<CirclePlus /> <CirclePlus />
</el-icon> </el-icon>
</el-button> </el-button>
@ -254,7 +254,7 @@
</template> </template>
<script setup> <script setup>
import { nextTick, onMounted, onUnmounted, ref } from 'vue' import { nextTick, onMounted, onUnmounted, reactive, ref } from 'vue'
import UrdfView from './UrdfView.vue' import UrdfView from './UrdfView.vue'
import { getPoseApi, getJointStateApi, moveJApi, torqueOnApi, speedJApi, stopMotionApi, speedLApi } from '@/api/arm' import { getPoseApi, getJointStateApi, moveJApi, torqueOnApi, speedJApi, stopMotionApi, speedLApi } from '@/api/arm'
import { Remove, CirclePlus } from '@element-plus/icons-vue' import { Remove, CirclePlus } from '@element-plus/icons-vue'
@ -497,6 +497,9 @@ onUnmounted(() => {
touch-action: none; touch-action: none;
-webkit-user-select: none; -webkit-user-select: none;
user-select: none; user-select: none;
min-width: 48px;
min-height: 48px;
font-size: 22px;
} }
// //
@ -589,6 +592,19 @@ onUnmounted(() => {
:deep(.el-input__wrapper) { :deep(.el-input__wrapper) {
background-color: $color-background-base; background-color: $color-background-base;
box-shadow: 0 0 0 1px $color-accent-border inset; box-shadow: 0 0 0 1px $color-accent-border inset;
min-height: 48px;
font-size: 17px;
}
:deep(.el-input-group__prepend),
:deep(.el-input-group__append) {
padding: 0;
}
.press-control {
width: 52px;
min-height: 48px;
margin: 0;
} }
} }
} }
@ -610,10 +626,10 @@ onUnmounted(() => {
border-left: 1px solid $color-accent-border; border-left: 1px solid $color-accent-border;
.controller { .controller {
margin-bottom: 60px; margin-bottom: 48px;
.title { .title {
font-size: 20px; font-size: 22px;
font-weight: bold; font-weight: bold;
margin-bottom: 10px; margin-bottom: 10px;
} }
@ -628,6 +644,7 @@ onUnmounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
min-height: 68px;
padding: 10px 14px; padding: 10px 14px;
background: $color-background-base; background: $color-background-base;
border: 1px solid $color-accent-border; border: 1px solid $color-accent-border;
@ -640,13 +657,13 @@ onUnmounted(() => {
} }
.axis-label { .axis-label {
width: 28px; width: 36px;
height: 28px; height: 36px;
border-radius: 6px; border-radius: 6px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: 13px; font-size: 16px;
font-weight: 700; font-weight: 700;
flex-shrink: 0; flex-shrink: 0;
} }
@ -671,11 +688,37 @@ onUnmounted(() => {
.axis-slider-wrap { .axis-slider-wrap {
position: relative;
flex: 1; flex: 1;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center;
min-width: 0;
.axis-name {
position: absolute;
left: 50%;
z-index: 1;
font-size: 16px;
white-space: nowrap;
pointer-events: none;
transform: translateX(-50%);
}
.axis-control-buttons {
position: relative;
z-index: 2;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 4px;
.press-control {
width: 50px;
height: 50px;
margin: 0;
}
}
} }
} }
} }
@ -683,6 +726,7 @@ onUnmounted(() => {
} }
.speed-container { .speed-container {
min-height: 48px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
@ -690,6 +734,7 @@ onUnmounted(() => {
.label, .label,
.proportion { .proportion {
width: 100px; width: 100px;
font-size: 16px;
text-align: center; text-align: center;
} }
@ -698,6 +743,15 @@ onUnmounted(() => {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
:deep(.el-slider) {
height: 40px;
}
:deep(.el-slider__button) {
width: 24px;
height: 24px;
}
.speed-tooltip { .speed-tooltip {
position: absolute; position: absolute;
bottom: calc(100% + 7px); bottom: calc(100% + 7px);

View File

@ -14,15 +14,20 @@ import VoiceConversation from './VoiceConversation.vue';
import { computed } from 'vue'; import { computed } from 'vue';
import { useRobotStore } from '@/stores/robot'; import { useRobotStore } from '@/stores/robot';
const VIDEO_SERVER_HOST = import.meta.env.VITE_VIDEO_SERVER_HOST || 'http://localhost:8080/'
const TerminalId = import.meta.env.VITE_TERMINAL_ID || 'c2f8a06826baf03843925c1a2a13bcfd'
const robotStore = useRobotStore() const robotStore = useRobotStore()
const videoUrl = computed(() => { const videoUrl = `${VIDEO_SERVER_HOST}/api/edge/camera/rgb-stream/public?terminalId=${TerminalId}&deviceId=${robotStore.cameraDeviceId}`
if (!robotStore.ip || !robotStore.cameraDeviceId) return ''
const params = new URLSearchParams({ // const videoUrl = computed(() => {
ip: robotStore.ip, // if (!robotStore.ip || !robotStore.cameraDeviceId) return ''
deviceId: robotStore.cameraDeviceId // const params = new URLSearchParams({
}) // ip: robotStore.ip,
return `/api/camera/video?${params}` // deviceId: robotStore.cameraDeviceId
}) // })
// return `/api/camera/video?${params}`
// })
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.page-container { .page-container {