Files
JoyD/AutoRobot/Windows/Robot/Web/src/views/HomePage.vue
2025-10-22 22:49:04 +08:00

398 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="min-h-screen max-h-screen flex flex-col bg-gradient-to-br from-gray-50 via-gray-100 to-blue-50 overflow-hidden">
<!-- 导航栏组件 -->
<Header
:is-connected="isConnected"
:ws-url="wsUrl"
@toggle-connection="toggleConnection"
/>
<!-- 主内容区 -->
<main class="flex-grow h-full container mx-auto px-4 sm:px-6 py-6">
<!-- 移动端连接状态条 -->
<div class="sm:hidden mb-4 p-3 rounded-xl bg-white/80 backdrop-blur-sm shadow-md border border-gray-100">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-2">
<span :class="connectionIconClass" class="animate-pulse"></span>
<span class="text-sm font-medium" :class="connectionTextClass">{{ connectionStatusText }}</span>
</div>
<div class="text-xs font-mono text-gray-500 truncate">
{{ wsUrl }}
</div>
</div>
</div>
<!-- 主内容区两列布局 -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 h-full w-full">
<!-- 标签页面板 - 占据2/3宽度 -->
<div class="lg:col-span-2 h-full">
<div class="h-full bg-white/90 backdrop-blur-sm rounded-2xl shadow-lg border border-gray-100">
<TabPage :tabs="tabs">
<template #tab-content="{ activeTab }">
<div v-if="activeTab === 0" class="bg-transparent h-[calc(100vh-8rem-4rem-0.5rem)] flex flex-col">
<!-- 消息显示区域 - 8rem=导航栏+页边距, 4rem=页脚, 3.5rem=标签栏 -->
<MessageArea :messages="messages" class="flex-grow" />
</div>
<div v-else-if="activeTab === 1" class="p-4 bg-transparent h-full">
<div v-if="screenshots.length > 0" class="h-full">
<!-- 只显示最新的一张截图 -->
<img
:src="getImageSource(screenshots[0].data)"
alt="手机截图"
class="w-full h-auto max-h-[calc(100vh-16rem)] object-contain rounded-lg shadow-md"
@error="handleImageError($event, screenshots[0].id)"
/>
</div>
<div v-else class="h-full flex items-center justify-center text-gray-400">
<p>暂无截图数据</p>
</div>
</div>
</template>
</TabPage>
</div>
</div>
<!-- 控制面板 - 占据1/3宽度 -->
<div class="h-full">
<div class="h-full bg-white/90 backdrop-blur-sm rounded-2xl shadow-lg border border-gray-100 overflow-y-auto">
<div class="p-4 space-y-6">
<!-- 发送消息卡片 -->
<SendMessage
:is-connected="isConnected"
@send-message="sendMessage"
/>
<!-- 手机端地址卡片 -->
<ConnectionInfo
:is-connected="isConnected"
:ws-url="wsUrl"
/>
<!-- 快捷操作卡片 -->
<QuickActions
:is-connected="isConnected"
@clear-messages="clearMessages"
@show-help="showConnectionHelp"
@take-phone-screenshot="takePhoneScreenshot"
/>
</div>
</div>
</div>
</div>
</main>
<!-- 页脚组件 -->
<Footer class="mt-0" />
<!-- WebSocket客户端组件无UI界面- 使用私有库组件 -->
<component
:is="WebSocketClient"
ref="wsClient"
:url="wsUrl"
:autoConnect="false"
@connect="handleConnect"
@disconnect="handleDisconnect"
@message="handleMessage"
@error="handleError"
@status-change="handleStatusChange"
/>
</div>
</template>
<script setup>
import { ref, computed, onMounted, onBeforeUnmount, nextTick } from 'vue';
// 导入所有组件
import Header from '../components/Header.vue';
import MessageArea from '../components/MessageArea.vue';
import SendMessage from '../components/SendMessage.vue';
import ConnectionInfo from '../components/ConnectionInfo.vue';
import QuickActions from '../components/QuickActions.vue';
import Footer from '../components/Footer.vue';
import TabPage from '../components/TabPage.vue';
// 从私有库导入WebSocketClient组件
import { WebSocketClient } from 'joyd.web.vue.websocket-client';
// 全局调试模式开关
window.DEBUG = true;
// 状态管理
const messages = ref([
{
id: 1,
sender: '系统',
content: '欢迎使用WebSocket通信中心请连接服务器开始交互',
time: new Date().toLocaleTimeString(),
isSystem: true
}
]);
const wsUrl = ref('ws://localhost:8805');
const isConnected = ref(false);
// 存储截图数据
const screenshots = ref([]);
// 标签页配置
const tabs = ref([
{
label: '消息面板',
},
{
label: '手机截图',
}
]);
// 创建WebSocketClient组件的引用
const wsClient = ref(null);
// 计算属性
const connectionIconClass = computed(() => {
return isConnected.value
? 'rounded-full bg-green-500'
: 'rounded-full bg-red-500';
});
const connectionTextClass = computed(() => {
return isConnected.value ? 'text-green-600' : 'text-red-600';
});
const connectionStatusText = computed(() => {
return isConnected.value ? '已连接' : '未连接';
});
// 方法
const addMessage = (sender, content, isSystem = false) => {
const now = new Date();
const timeString = now.toLocaleTimeString();
messages.value.push({
id: Date.now(),
sender,
content,
time: timeString,
isSystem
});
};
// WebSocket事件处理函数
const handleConnect = (url) => {
addMessage('系统', '已成功连接到WebSocket服务器', true);
isConnected.value = true;
};
const handleDisconnect = (code, reason) => {
isConnected.value = false;
let closeMessage = '连接已断开';
if (code === 1006) {
closeMessage = '连接异常断开';
}
addMessage('系统', closeMessage, true);
};
const handleMessage = (data) => {
// 检查是否为二进制数据(截图)
if (data instanceof Blob) {
addMessage('系统', '收到手机截图', true);
// 将二进制数据添加到列表中
screenshots.value.unshift({
id: Date.now(),
data: data,
timestamp: new Date().toLocaleString()
});
} else {
// 处理文本消息
addMessage('服务器', data);
}
};
const handleError = (errorMessage) => {
addMessage('系统', errorMessage, true);
};
const handleStatusChange = (connected) => {
isConnected.value = connected;
};
// 切换连接状态
const toggleConnection = () => {
if (wsClient.value) {
wsClient.value.toggleConnection();
}
};
// 发送消息
const sendMessage = (message) => {
if (!message || !wsClient.value || !isConnected.value) {
return;
}
try {
// 显示发送中状态
const sendingMessage = {
id: Date.now(),
sender: 'me',
content: message + ' <span class="text-xs text-gray-400">发送中...</span>',
time: new Date().toLocaleTimeString(),
isSystem: false,
sending: true
};
messages.value.push(sendingMessage);
// 发送消息
const success = wsClient.value.send(message);
// 更新消息状态
setTimeout(() => {
const index = messages.value.findIndex(m => m.id === sendingMessage.id);
if (index !== -1) {
messages.value.splice(index, 1);
addMessage('me', message);
}
}, 300);
} catch (error) {
addMessage('系统', '消息发送失败', true);
// 移除发送中状态的消息
const index = messages.value.findIndex(m => m.sending);
if (index !== -1) {
messages.value.splice(index, 1);
}
}
}
// 发送手机截屏命令
const takePhoneScreenshot = () => {
if (!wsClient.value || !isConnected.value) {
return;
}
try {
// 显示命令发送状态
addMessage('me', '发送手机截屏命令', true);
// 生成唯一的消息ID
const messageId = Date.now().toString();
// 发送符合 messageId:content 格式的消息
const screenshotCommand = messageId + ':screenshot';
const success = wsClient.value.send(screenshotCommand);
// 记录命令发送状态
if (success) {
addMessage('系统', '手机截屏命令已发送', true);
} else {
addMessage('系统', '手机截屏命令发送失败', true);
}
} catch (error) {
addMessage('系统', '手机截屏命令发送失败', true);
}
};
// 事件处理方法
const clearMessages = () => {
if (confirm('确定要清空所有消息吗?')) {
messages.value = [
{
id: Date.now(),
sender: '系统',
content: '消息已清空',
time: new Date().toLocaleTimeString(),
isSystem: true
}
];
}
};
// 获取图片源处理二进制Blob数据
const getImageSource = (data) => {
if (data instanceof Blob) {
// 为二进制数据创建一个临时的URL
return URL.createObjectURL(data);
} else {
// 向后兼容如果数据是字符串继续处理base64编码
// 清理Base64数据移除可能的无效字符和换行符
let cleanedData = data.replace(/[\r\n\s]/g, '');
// 检查是否已经包含了data:image前缀
if (cleanedData.startsWith('data:image')) {
return cleanedData;
}
// 尝试添加不同的MIME类型
// 首先尝试标准的JPEG
return 'data:image/jpeg;base64,' + cleanedData;
}
};
// 处理图片加载错误
const handleImageError = (event, screenshotId) => {
// 显示错误提示
event.target.alt = '图片加载失败';
event.target.src = '';
};
// 组件卸载时清理创建的object URLs
onBeforeUnmount(() => {
// 清理所有创建的object URLs
screenshots.value.forEach(screenshot => {
if (screenshot.data instanceof Blob) {
// 注意我们无法直接访问已创建的URL所以这里不做清理
// 在实际项目中应该在创建URL时保存它的引用
}
});
// 关闭WebSocket连接
if (wsClient.value) {
wsClient.value.disconnect();
}
});
// 保存截图到本地
const saveScreenshot = (screenshotData) => {
try {
// 创建一个临时的下载链接
const link = document.createElement('a');
if (screenshotData instanceof Blob) {
// 对于二进制数据使用URL.createObjectURL
link.href = URL.createObjectURL(screenshotData);
// 设置合适的文件名和扩展名
link.download = `screenshot_${new Date().toISOString().replace(/:/g, '-')}.jpg`;
} else {
// 对于字符串数据向后兼容使用getImageSource
link.href = getImageSource(screenshotData);
link.download = `screenshot_${new Date().toISOString().replace(/:/g, '-')}.jpg`;
}
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// 保存完成后清理URL对象仅针对Blob
if (screenshotData instanceof Blob) {
URL.revokeObjectURL(link.href);
}
} catch (error) {
addMessage('系统', '保存截图失败', true);
}
};
const showConnectionHelp = () => {
alert('WebSocket连接帮助:\n\n1. 点击"连接服务器"按钮建立连接\n2. 连接成功后可以发送和接收消息\n3. 确保服务器正在运行并且端口8805可访问\n\n如有问题请检查网络连接和服务器状态。');
};
// 生命周期钩子
onMounted(() => {
// 组件挂载时尝试自动连接服务器
toggleConnection();
});
onBeforeUnmount(() => {
// 组件卸载时关闭WebSocket连接
if (wsClient.value) {
wsClient.value.disconnect();
}
});
</script>