feat: 添加功能底栏
This commit is contained in:
parent
cbb3543c4a
commit
bbad7388d8
|
@ -21,6 +21,7 @@ declare module 'vue' {
|
|||
DiagramCanvas: typeof import('./src/components/LabCanvas/DiagramCanvas.vue')['default']
|
||||
Dialog: typeof import('./src/components/Dialog.vue')['default']
|
||||
ETH: typeof import('./src/components/equipments/ETH.vue')['default']
|
||||
FunctionBar: typeof import('./src/components/FunctionBar.vue')['default']
|
||||
HDMI: typeof import('./src/components/equipments/HDMI.vue')['default']
|
||||
IpInputField: typeof import('./src/components/InputField/IpInputField.vue')['default']
|
||||
LabCanvas: typeof import('./src/components/LabCanvasNew/LabCanvas.vue')['default']
|
||||
|
@ -42,6 +43,11 @@ declare module 'vue' {
|
|||
RekaSplitterGroup: typeof import('reka-ui')['SplitterGroup']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
ScrollAreaCorner: typeof import('reka-ui')['ScrollAreaCorner']
|
||||
ScrollAreaRoot: typeof import('reka-ui')['ScrollAreaRoot']
|
||||
ScrollAreaScrollbar: typeof import('reka-ui')['ScrollAreaScrollbar']
|
||||
ScrollAreaThumb: typeof import('reka-ui')['ScrollAreaThumb']
|
||||
ScrollAreaViewport: typeof import('reka-ui')['ScrollAreaViewport']
|
||||
SD: typeof import('./src/components/equipments/SD.vue')['default']
|
||||
SevenSegmentDisplay: typeof import('./src/components/equipments/SevenSegmentDisplay.vue')['default']
|
||||
SFP: typeof import('./src/components/equipments/SFP.vue')['default']
|
||||
|
@ -52,6 +58,11 @@ declare module 'vue' {
|
|||
SplitterPanel: typeof import('reka-ui')['SplitterPanel']
|
||||
SplitterResizeHandle: typeof import('reka-ui')['SplitterResizeHandle']
|
||||
Switch: typeof import('./src/components/equipments/Switch.vue')['default']
|
||||
TabsContent: typeof import('reka-ui')['TabsContent']
|
||||
TabsIndicator: typeof import('reka-ui')['TabsIndicator']
|
||||
TabsList: typeof import('reka-ui')['TabsList']
|
||||
TabsRoot: typeof import('reka-ui')['TabsRoot']
|
||||
TabsTrigger: typeof import('reka-ui')['TabsTrigger']
|
||||
ThemeControlButton: typeof import('./src/components/ThemeControlButton.vue')['default']
|
||||
ThemeControlToggle: typeof import('./src/components/ThemeControlToggle.vue')['default']
|
||||
TutorialCarousel: typeof import('./src/components/TutorialCarousel.vue')['default']
|
||||
|
|
|
@ -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">
|
||||
<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-panel-canvas"
|
||||
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" />
|
||||
<DiagramCanvas
|
||||
ref="diagramCanvas"
|
||||
:showDocPanel="showDocPanel"
|
||||
@diagram-updated="handleDiagramUpdated"
|
||||
@open-components="openComponentsMenu"
|
||||
@toggle-doc-panel="toggleDocPanel"
|
||||
/>
|
||||
</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-h-resize-handle"
|
||||
class="w-2 bg-base-100 hover:bg-primary hover:opacity-70 transition-colors"
|
||||
/>
|
||||
<!-- 右侧编辑区域 -->
|
||||
<SplitterPanel
|
||||
id="splitter-group-panel-properties"
|
||||
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">
|
||||
<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-v-resize-handle"
|
||||
class="h-2 bg-base-100 hover:bg-primary hover:opacity-70 transition-colors"
|
||||
/>
|
||||
|
||||
<!-- 功能底栏 -->
|
||||
<SplitterPanel
|
||||
id="splitter-group-v-panel-bar"
|
||||
:default-size="20"
|
||||
:min-size="15"
|
||||
class="w-full overflow-hidden"
|
||||
>
|
||||
<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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue