Files
JoyD/AutoRobot/Windows/Robot/Web/src/views/HomePage.vue

398 lines
12 KiB
Vue
Raw Normal View History

<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组件
2025-10-22 22:49:04 +08:00
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>