feat: 添加功能底栏
This commit is contained in:
70
src/components/FunctionBar.vue
Normal file
70
src/components/FunctionBar.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div class="h-full flex flex-col">
|
||||
<div class="tabs tabs-box flex-shrink-0">
|
||||
<label class="tab">
|
||||
<input
|
||||
type="radio"
|
||||
name="function-bar"
|
||||
id="1"
|
||||
checked
|
||||
@change="handleTabChange"
|
||||
/>
|
||||
<TerminalIcon class="icon" />
|
||||
日志终端
|
||||
</label>
|
||||
<label class="tab">
|
||||
<input
|
||||
type="radio"
|
||||
name="function-bar"
|
||||
id="2"
|
||||
@change="handleTabChange"
|
||||
/>
|
||||
<VideoIcon class="icon" />
|
||||
HTTP视频流
|
||||
</label>
|
||||
<label class="tab">
|
||||
<input
|
||||
type="radio"
|
||||
name="function-bar"
|
||||
id="3"
|
||||
@change="handleTabChange"
|
||||
/>
|
||||
<SquareActivityIcon class="icon" />
|
||||
示波器
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex-1 overflow-hidden">
|
||||
<div v-if="checkID === 1" class="h-full overflow-y-auto"></div>
|
||||
<div v-else-if="checkID === 2" class="h-full overflow-y-auto">
|
||||
<VideoStreamView />
|
||||
</div>
|
||||
<div v-else-if="checkID === 3" class="h-full overflow-y-auto">
|
||||
<OscilloscopeView />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { VideoIcon, SquareActivityIcon, TerminalIcon } from "lucide-vue-next";
|
||||
import VideoStreamView from "@/views/VideoStreamView.vue";
|
||||
import OscilloscopeView from "@/views/OscilloscopeView.vue";
|
||||
import { isNull, toNumber } from "lodash";
|
||||
import { ref } from "vue";
|
||||
|
||||
const checkID = ref(1);
|
||||
|
||||
function handleTabChange(event: Event) {
|
||||
const target = event.currentTarget as HTMLInputElement;
|
||||
if (isNull(target)) return;
|
||||
|
||||
checkID.value = toNumber(target.id);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import "../assets/main.css";
|
||||
|
||||
.icon {
|
||||
@apply h-4 w-4 opacity-70 mr-1.5;
|
||||
}
|
||||
</style>
|
||||
@@ -50,18 +50,6 @@
|
||||
测试功能
|
||||
</router-link>
|
||||
</li>
|
||||
<li class="my-1 hover:translate-x-1 transition-all duration-300">
|
||||
<router-link to="/video-stream" class="text-base font-medium">
|
||||
<Video class="icon" />
|
||||
HTTP视频流
|
||||
</router-link>
|
||||
</li>
|
||||
<li class="my-1 hover:translate-x-1 transition-all duration-300">
|
||||
<router-link to="/oscilloscope" class="text-base font-medium">
|
||||
<SquareActivity class="icon" />
|
||||
示波器
|
||||
</router-link>
|
||||
</li>
|
||||
<li class="my-1 hover:translate-x-1 transition-all duration-300">
|
||||
<router-link to="/markdown-test" class="text-base font-medium">
|
||||
<FileText class="icon" />
|
||||
|
||||
@@ -19,8 +19,6 @@ const router = createRouter({
|
||||
{ path: "/test", name: "test", component: TestView },
|
||||
{ path: "/user", name: "user", component: UserView },
|
||||
{ path: "/admin", name: "admin", component: AdminView },
|
||||
{ path: "/video-stream", name: "videoStream", component: VideoStreamView },
|
||||
{ path: "/oscilloscope", name: "oscilloscope", component: OscilloscopeView },
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@@ -1,41 +1,89 @@
|
||||
<template>
|
||||
<div class="h-screen flex flex-col overflow-hidden">
|
||||
<div class="flex flex-1 overflow-hidden relative">
|
||||
<SplitterGroup id="splitter-group" direction="horizontal" class="w-full h-full">
|
||||
<!-- 左侧图形化区域 -->
|
||||
<SplitterPanel
|
||||
id="splitter-group-panel-canvas"
|
||||
:default-size="60"
|
||||
:min-size="30"
|
||||
class="relative bg-base-200 overflow-hidden h-full"
|
||||
>
|
||||
<DiagramCanvas ref="diagramCanvas" :showDocPanel="showDocPanel"
|
||||
@diagram-updated="handleDiagramUpdated" @open-components="openComponentsMenu"
|
||||
@toggle-doc-panel="toggleDocPanel" />
|
||||
<SplitterGroup
|
||||
id="splitter-group-v"
|
||||
direction="vertical"
|
||||
class="w-full h-full"
|
||||
>
|
||||
<SplitterPanel id="splitter-group-v-panel-project">
|
||||
<SplitterGroup
|
||||
id="splitter-group-h"
|
||||
direction="horizontal"
|
||||
class="w-full h-full"
|
||||
>
|
||||
<!-- 左侧图形化区域 -->
|
||||
<SplitterPanel
|
||||
id="splitter-group-h-panel-canvas"
|
||||
:default-size="60"
|
||||
:min-size="30"
|
||||
class="relative bg-base-200 overflow-hidden h-full"
|
||||
>
|
||||
<DiagramCanvas
|
||||
ref="diagramCanvas"
|
||||
:showDocPanel="showDocPanel"
|
||||
@diagram-updated="handleDiagramUpdated"
|
||||
@open-components="openComponentsMenu"
|
||||
@toggle-doc-panel="toggleDocPanel"
|
||||
/>
|
||||
</SplitterPanel>
|
||||
<!-- 拖拽分割线 -->
|
||||
<SplitterResizeHandle
|
||||
id="splitter-group-h-resize-handle"
|
||||
class="w-2 bg-base-100 hover:bg-primary hover:opacity-70 transition-colors"
|
||||
/>
|
||||
<!-- 右侧编辑区域 -->
|
||||
<SplitterPanel
|
||||
id="splitter-group-h-panel-properties"
|
||||
:min-size="20"
|
||||
class="bg-base-200 h-full overflow-hidden flex flex-col"
|
||||
>
|
||||
<div class="overflow-y-auto flex-1">
|
||||
<!-- 使用条件渲染显示不同的面板 -->
|
||||
<PropertyPanel
|
||||
v-show="!showDocPanel"
|
||||
:componentData="componentManager.selectedComponentData.value"
|
||||
:componentConfig="
|
||||
componentManager.selectedComponentConfig.value
|
||||
"
|
||||
@updateProp="updateComponentProp"
|
||||
@updateDirectProp="updateComponentDirectProp"
|
||||
/>
|
||||
<div
|
||||
v-show="showDocPanel"
|
||||
class="doc-panel overflow-y-auto h-full"
|
||||
>
|
||||
<MarkdownRenderer :content="documentContent" />
|
||||
</div>
|
||||
</div>
|
||||
</SplitterPanel>
|
||||
</SplitterGroup>
|
||||
</SplitterPanel>
|
||||
<!-- 拖拽分割线 -->
|
||||
<SplitterResizeHandle id="splitter-group-resize-handle" class="w-2 bg-base-100 hover:bg-primary hover:opacity-70 transition-colors" />
|
||||
<!-- 右侧编辑区域 -->
|
||||
|
||||
<SplitterResizeHandle
|
||||
id="splitter-group-v-resize-handle"
|
||||
class="h-2 bg-base-100 hover:bg-primary hover:opacity-70 transition-colors"
|
||||
/>
|
||||
|
||||
<!-- 功能底栏 -->
|
||||
<SplitterPanel
|
||||
id="splitter-group-panel-properties"
|
||||
:min-size="20"
|
||||
class="bg-base-200 h-full overflow-hidden flex flex-col"
|
||||
id="splitter-group-v-panel-bar"
|
||||
:default-size="20"
|
||||
:min-size="15"
|
||||
class="w-full overflow-hidden"
|
||||
>
|
||||
<div class="overflow-y-auto flex-1">
|
||||
<!-- 使用条件渲染显示不同的面板 -->
|
||||
<PropertyPanel v-show="!showDocPanel" :componentData="componentManager.selectedComponentData.value"
|
||||
:componentConfig="componentManager.selectedComponentConfig.value" @updateProp="updateComponentProp"
|
||||
@updateDirectProp="updateComponentDirectProp" />
|
||||
<div v-show="showDocPanel" class="doc-panel overflow-y-auto h-full">
|
||||
<MarkdownRenderer :content="documentContent" />
|
||||
</div>
|
||||
</div>
|
||||
<FunctionBar class="mx-4 mt-1" />
|
||||
</SplitterPanel>
|
||||
</SplitterGroup>
|
||||
</div>
|
||||
<!-- 元器件选择组件 -->
|
||||
<ComponentSelector :open="showComponentsMenu" @update:open="showComponentsMenu = $event"
|
||||
@add-component="handleAddComponent" @add-template="handleAddTemplate" @close="showComponentsMenu = false" />
|
||||
<ComponentSelector
|
||||
:open="showComponentsMenu"
|
||||
@update:open="showComponentsMenu = $event"
|
||||
@add-component="handleAddComponent"
|
||||
@add-template="handleAddTemplate"
|
||||
@close="showComponentsMenu = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -46,16 +94,19 @@ import DiagramCanvas from "@/components/LabCanvas/DiagramCanvas.vue";
|
||||
import ComponentSelector from "@/components/LabCanvas/ComponentSelector.vue";
|
||||
import PropertyPanel from "@/components/PropertyPanel.vue";
|
||||
import MarkdownRenderer from "@/components/MarkdownRenderer.vue";
|
||||
import FunctionBar from "@/components/FunctionBar.vue";
|
||||
import { useProvideComponentManager } from "@/components/LabCanvas";
|
||||
import type { DiagramData, DiagramPart } from "@/components/LabCanvas";
|
||||
import type { DiagramData } from "@/components/LabCanvas";
|
||||
import { useAlertStore } from "@/components/Alert";
|
||||
|
||||
// 获取路由参数
|
||||
import { useRoute } from "vue-router";
|
||||
const route = useRoute();
|
||||
|
||||
// 提供组件管理服务
|
||||
const componentManager = useProvideComponentManager();
|
||||
|
||||
const alert = useAlertStore();
|
||||
|
||||
// --- 文档面板控制 ---
|
||||
const showDocPanel = ref(false);
|
||||
const documentContent = ref("");
|
||||
@@ -106,34 +157,6 @@ async function loadDocumentContent() {
|
||||
const showComponentsMenu = ref(false);
|
||||
const diagramCanvas = ref(null);
|
||||
|
||||
// --- 页面动画和通知 ---
|
||||
const showNotification = ref(false);
|
||||
const notificationMessage = ref("");
|
||||
const notificationType = ref<"success" | "error" | "info">("info");
|
||||
|
||||
function showToast(
|
||||
message: string,
|
||||
type: "success" | "error" | "info" = "info",
|
||||
duration = 3000,
|
||||
) {
|
||||
const canvasInstance = diagramCanvas.value as any;
|
||||
if (canvasInstance && canvasInstance.showToast) {
|
||||
canvasInstance.showToast(message, type, duration);
|
||||
} else {
|
||||
// 后备方案:使用原来的通知系统
|
||||
notificationMessage.value = message;
|
||||
notificationType.value = type;
|
||||
showNotification.value = true;
|
||||
|
||||
// 设置自动消失
|
||||
setTimeout(() => {
|
||||
showNotification.value = false;
|
||||
}, duration);
|
||||
}
|
||||
}
|
||||
|
||||
// --- 事件处理器(委托给组件管理器) ---
|
||||
|
||||
function openComponentsMenu() {
|
||||
showComponentsMenu.value = true;
|
||||
}
|
||||
@@ -155,7 +178,7 @@ async function handleAddTemplate(templateData: {
|
||||
}) {
|
||||
const result = await componentManager.addTemplate(templateData);
|
||||
if (result) {
|
||||
showToast(result.message, result.success ? "success" : "error");
|
||||
alert?.show(result.message, result.success ? "success" : "error");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user