add: markdown viewer
This commit is contained in:
parent
48ae3b5975
commit
7dd5e2189f
|
@ -10,8 +10,10 @@
|
|||
"dependencies": {
|
||||
"@svgdotjs/svg.js": "^3.2.4",
|
||||
"@types/lodash": "^4.17.16",
|
||||
"all": "^0.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"log-symbols": "^7.0.0",
|
||||
"marked": "^12.0.0",
|
||||
"mathjs": "^14.4.0",
|
||||
"pinia": "^3.0.1",
|
||||
"tinypool": "^1.0.2",
|
||||
|
@ -1993,6 +1995,12 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/all": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/all/-/all-0.0.0.tgz",
|
||||
"integrity": "sha512-0oKlfNVv2d+d7c1gwjGspzgbwot47PGQ4b3v1ccx4mR8l9P/Y6E6Dr/yE8lNT63EcAKEbHo6UG3odDpC/NQcKw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
||||
|
@ -3061,6 +3069,18 @@
|
|||
"@jridgewell/sourcemap-codec": "^1.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "12.0.2",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz",
|
||||
"integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/mathjs": {
|
||||
"version": "14.4.0",
|
||||
"resolved": "https://registry.npmjs.org/mathjs/-/mathjs-14.4.0.tgz",
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
"dependencies": {
|
||||
"@svgdotjs/svg.js": "^3.2.4",
|
||||
"@types/lodash": "^4.17.16",
|
||||
"all": "^0.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"log-symbols": "^7.0.0",
|
||||
"marked": "^12.0.0",
|
||||
"mathjs": "^14.4.0",
|
||||
"pinia": "^3.0.1",
|
||||
"tinypool": "^1.0.2",
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
@mousedown="handleCanvasMouseDown"
|
||||
@mousedown.middle.prevent="startMiddleDrag"
|
||||
@wheel.prevent="onZoom"
|
||||
@contextmenu.prevent="handleContextMenu">
|
||||
<!-- 工具栏 -->
|
||||
@contextmenu.prevent="handleContextMenu"> <!-- 工具栏 -->
|
||||
<div class="absolute top-2 right-2 flex gap-2 z-30">
|
||||
<button class="btn btn-sm btn-primary" @click="openDiagramFileSelector">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
|
@ -23,6 +22,11 @@
|
|||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
添加组件
|
||||
</button> <button class="btn btn-sm btn-primary" @click="emit('toggle-doc-panel')">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
{{ props.showDocPanel ? '属性面板' : '文档' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -148,11 +152,12 @@ function handleContextMenu(e: MouseEvent) {
|
|||
}
|
||||
|
||||
// 定义组件发出的事件
|
||||
const emit = defineEmits(['diagram-updated', 'component-selected', 'component-moved', 'component-delete', 'wire-created', 'wire-deleted', 'load-component-module', 'open-components']);
|
||||
const emit = defineEmits(['diagram-updated', 'component-selected', 'component-moved', 'component-delete', 'wire-created', 'wire-deleted', 'load-component-module', 'open-components', 'toggle-doc-panel']);
|
||||
|
||||
// 定义组件接受的属性
|
||||
const props = defineProps<{
|
||||
componentModules: Record<string, any>
|
||||
componentModules: Record<string, any>;
|
||||
showDocPanel?: boolean; // 添加属性接收文档面板的显示状态
|
||||
}>();
|
||||
|
||||
// --- 画布状态 ---
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { marked } from 'marked';
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const renderedContent = computed(() => {
|
||||
if (!props.content) return '<p>没有内容</p>';
|
||||
let processedContent = props.content;
|
||||
// 设置 marked 选项
|
||||
const renderer = new marked.Renderer();
|
||||
marked.setOptions({
|
||||
renderer: renderer,
|
||||
gfm: true,
|
||||
breaks: true
|
||||
});
|
||||
return marked(processedContent);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="markdown-content" v-html="renderedContent"></div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.markdown-content {
|
||||
color: hsl(var(--bc));
|
||||
line-height: 1.6;
|
||||
padding: 1rem 1.5rem;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.markdown-content :deep(img) {
|
||||
max-width: 60%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 1rem auto;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
.markdown-content :deep(h1) {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
color: hsl(var(--bc));
|
||||
font-weight: 700;
|
||||
font-size: 2rem;
|
||||
line-height: 1.3;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid hsl(var(--b2));
|
||||
}
|
||||
|
||||
.markdown-content :deep(h2) {
|
||||
margin-top: 1.8rem;
|
||||
margin-bottom: 0.8rem;
|
||||
color: hsl(var(--bc));
|
||||
font-weight: 600;
|
||||
font-size: 1.5rem;
|
||||
line-height: 1.4;
|
||||
padding-left: 0.5rem;
|
||||
border-left: 4px solid hsl(var(--p));
|
||||
}
|
||||
|
||||
.markdown-content :deep(h3) {
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: hsl(var(--bc));
|
||||
font-weight: 600;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.4;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.markdown-content :deep(h4),
|
||||
.markdown-content :deep(h5),
|
||||
.markdown-content :deep(h6) {
|
||||
margin-top: 1.2rem;
|
||||
margin-bottom: 0.6rem;
|
||||
color: hsl(var(--bc));
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.5;
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
|
||||
.markdown-content :deep(p) {
|
||||
text-indent: 2em;
|
||||
margin: 1rem 0;
|
||||
color: hsl(var(--bc) / 0.8);
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.markdown-content :deep(ul),
|
||||
.markdown-content :deep(ol) {
|
||||
padding-left: 2em;
|
||||
margin: 0.75rem 0;
|
||||
color: hsl(var(--bc) / 0.8);
|
||||
}
|
||||
|
||||
.markdown-content :deep(li) {
|
||||
margin: 0.4rem 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.markdown-content :deep(ul ul),
|
||||
.markdown-content :deep(ul ol),
|
||||
.markdown-content :deep(ol ul),
|
||||
.markdown-content :deep(ol ol) {
|
||||
margin: 0.4rem 0 0.4rem 1rem;
|
||||
}
|
||||
|
||||
.markdown-content :deep(ul) {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
.markdown-content :deep(ol) {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
.markdown-content :deep(ul ul) {
|
||||
list-style-type: circle;
|
||||
}
|
||||
|
||||
.markdown-content :deep(ul ul ul) {
|
||||
list-style-type: square;
|
||||
}
|
||||
|
||||
.markdown-content :deep(ul li::marker) {
|
||||
color: hsl(var(--p));
|
||||
}
|
||||
|
||||
.markdown-content :deep(pre) {
|
||||
background-color: hsl(var(--b3));
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
overflow-x: auto;
|
||||
border: 1px solid hsl(var(--b2));
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.markdown-content :deep(code) {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
background-color: hsl(var(--b3));
|
||||
padding: 2px 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
font-size: 0.9em;
|
||||
color: hsl(var(--p));
|
||||
}
|
||||
|
||||
.markdown-content :deep(table) {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 1rem 0;
|
||||
background-color: hsl(var(--b1));
|
||||
border-radius: 0.5rem;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
.markdown-content :deep(th),
|
||||
.markdown-content :deep(td) {
|
||||
border: 1px solid hsl(var(--b2));
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown-content :deep(th) {
|
||||
background-color: hsl(var(--b2));
|
||||
font-weight: 500;
|
||||
color: hsl(var(--bc));
|
||||
}
|
||||
|
||||
.markdown-content :deep(td) {
|
||||
color: hsl(var(--bc) / 0.8);
|
||||
}
|
||||
|
||||
.markdown-content :deep(blockquote) {
|
||||
margin: 1rem 0;
|
||||
padding: 0.5rem 1rem;
|
||||
border-left: 4px solid hsl(var(--p));
|
||||
background-color: hsl(var(--b2));
|
||||
color: hsl(var(--bc) / 0.8);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.markdown-content :deep(hr) {
|
||||
border: none;
|
||||
border-top: 1px solid hsl(var(--b2));
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
.markdown-content :deep(a) {
|
||||
color: hsl(var(--p));
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.markdown-content :deep(a:hover) {
|
||||
color: hsl(var(--pf));
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
|
@ -1,13 +1,12 @@
|
|||
<template>
|
||||
<div class="h-screen flex flex-col overflow-hidden">
|
||||
<div class="flex flex-1 overflow-hidden relative">
|
||||
<!-- 左侧图形化区域 -->
|
||||
<div class="relative bg-base-200 overflow-hidden h-full" :style="{ width: leftPanelWidth + '%' }">
|
||||
<DiagramCanvas ref="diagramCanvas" :componentModules="componentModules"
|
||||
<!-- 左侧图形化区域 --> <div class="relative bg-base-200 overflow-hidden h-full" :style="{ width: leftPanelWidth + '%' }">
|
||||
<DiagramCanvas ref="diagramCanvas" :componentModules="componentModules" :showDocPanel="showDocPanel"
|
||||
@component-selected="handleComponentSelected" @component-moved="handleComponentMoved"
|
||||
@component-delete="handleComponentDelete" @wire-created="handleWireCreated" @wire-deleted="handleWireDeleted"
|
||||
@diagram-updated="handleDiagramUpdated" @open-components="openComponentsMenu"
|
||||
@load-component-module="handleLoadComponentModule" />
|
||||
@load-component-module="handleLoadComponentModule" @toggle-doc-panel="toggleDocPanel" />
|
||||
</div>
|
||||
|
||||
<!-- 拖拽分割线 -->
|
||||
|
@ -18,8 +17,12 @@
|
|||
<!-- 右侧编辑区域 -->
|
||||
<div class="bg-base-200 h-full overflow-hidden flex flex-col" :style="{ width: 100 - leftPanelWidth + '%' }">
|
||||
<div class="overflow-y-auto flex-1">
|
||||
<PropertyPanel :componentData="selectedComponentData" :componentConfig="selectedComponentConfig"
|
||||
<!-- 使用条件渲染显示不同的面板 -->
|
||||
<PropertyPanel v-if="!showDocPanel" :componentData="selectedComponentData" :componentConfig="selectedComponentConfig"
|
||||
@updateProp="updateComponentProp" @updateDirectProp="updateComponentDirectProp" />
|
||||
<div v-else class="doc-panel overflow-y-auto bg-base-100 rounded-md h-full">
|
||||
<MarkdownRenderer :content="documentContent" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -36,6 +39,7 @@ import { ref, computed, onMounted, onUnmounted, shallowRef } from "vue"; // 引
|
|||
import DiagramCanvas from "@/components/DiagramCanvas.vue";
|
||||
import ComponentSelector from "@/components/ComponentSelector.vue";
|
||||
import PropertyPanel from "@/components/PropertyPanel.vue";
|
||||
import MarkdownRenderer from "@/components/MarkdownRenderer.vue";
|
||||
import type { DiagramData, DiagramPart } from "@/components/diagramManager";
|
||||
import {
|
||||
type PropertyConfig,
|
||||
|
@ -44,6 +48,164 @@ import {
|
|||
generatePropsFromAttrs,
|
||||
} from "@/components/equipments/componentConfig"; // 引入组件配置工具
|
||||
|
||||
// --- 文档面板控制 ---
|
||||
const showDocPanel = ref(false);
|
||||
const documentContent = ref('');
|
||||
|
||||
// 切换文档面板和属性面板
|
||||
function toggleDocPanel() {
|
||||
showDocPanel.value = !showDocPanel.value;
|
||||
|
||||
// 如果切换到文档面板,则获取文档内容
|
||||
if (showDocPanel.value) {
|
||||
fetchDocumentation();
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟获取文档的函数
|
||||
function fetchDocumentation() {
|
||||
// 这里模拟API请求,返回固定的Markdown内容
|
||||
const mockDocContent = `
|
||||
# FPGA WebLab 用户指南
|
||||
|
||||
## 简介
|
||||
|
||||
FPGA WebLab是一个基于Web的FPGA设计和实验平台,允许用户在浏览器中进行FPGA设计、仿真和验证。该平台提供了丰富的组件库和直观的界面,使FPGA学习和设计变得简单高效。
|
||||
|
||||
## 功能特点
|
||||
|
||||
- **可视化设计**:拖拽式界面,无需编写底层代码
|
||||
- **组件库**:包含多种常用FPGA组件
|
||||
- **实时验证**:设计完成后可立即进行验证和测试
|
||||
- **远程编程**:支持远程将设计烧录到物理FPGA设备
|
||||
- **协作功能**:支持多人同时在线编辑和查看
|
||||
|
||||
## 快速入门
|
||||
|
||||
### 创建项目
|
||||
|
||||
1. 点击主界面的"新建项目"按钮
|
||||
2. 输入项目名称和描述
|
||||
3. 选择目标FPGA设备型号
|
||||
4. 点击"创建"完成项目初始化
|
||||
|
||||
### 添加组件
|
||||
|
||||
1. 在左侧组件库中找到需要的组件
|
||||
2. 将组件拖拽到设计区域
|
||||
3. 通过右侧属性面板配置组件参数
|
||||
|
||||
### 连接组件
|
||||
|
||||
1. 点击一个组件的输出引脚
|
||||
2. 拖动连线到目标组件的输入引脚
|
||||
3. 松开鼠标完成连接
|
||||
4. 连线会自动显示信号名称和类型
|
||||
|
||||
### 验证设计
|
||||
|
||||
1. 点击工具栏中的"验证"按钮
|
||||
2. 系统会自动检查设计中的错误和警告
|
||||
3. 如有问题,可查看错误日志进行修复
|
||||
|
||||
### 模拟仿真
|
||||
|
||||
1. 点击"仿真"按钮进入仿真模式
|
||||
2. 设置输入信号和仿真参数
|
||||
3. 运行仿真并查看波形输出
|
||||
4. 可通过时间轴查看不同时刻的信号状态
|
||||
|
||||
### 烧录到设备
|
||||
|
||||
1. 确保FPGA设备已连接
|
||||
2. 点击"合成"按钮生成比特流文件
|
||||
3. 点击"烧录"将设计下载到物理设备
|
||||
4. 查看烧录日志确认操作成功
|
||||
|
||||
## 组件说明
|
||||
|
||||
### 基础逻辑组件
|
||||
|
||||
- **AND/OR/XOR门**:基本逻辑门,可设置输入数量
|
||||
- **MUX多路复用器**:数据选择器,根据选择信号输出不同输入
|
||||
- **触发器/寄存器**:存储单元,可选D、T、JK等不同类型
|
||||
- **计数器**:可配置位宽、计数方向等参数
|
||||
|
||||
### 接口组件
|
||||
|
||||
- **按钮/开关**:用户输入控制元件
|
||||
- **LED指示灯**:显示数字输出状态
|
||||
- **七段数码管**:显示数字信息
|
||||
- **UART接口**:串行通信接口
|
||||
|
||||
### 高级组件
|
||||
|
||||
- **RAM/ROM**:存储模块,可配置数据位宽和深度
|
||||
- **PLL/DCM**:时钟控制模块
|
||||
- **DSP模块**:数字信号处理单元
|
||||
- **MCU集成**:可嵌入微控制器核心
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 设计无法编译
|
||||
|
||||
检查以下可能的原因:
|
||||
- 组件之间连线不完整
|
||||
- 组件参数设置不正确
|
||||
- 存在时序冲突
|
||||
- 资源使用超出目标设备限制
|
||||
|
||||
### 模拟结果与预期不符
|
||||
|
||||
可能的解决方法:
|
||||
- 检查输入激励是否正确
|
||||
- 验证时钟设置和复位信号
|
||||
- 调整仿真时间步长
|
||||
- 查看信号完整传播路径
|
||||
|
||||
### 设备烧录失败
|
||||
|
||||
常见原因及解决方法:
|
||||
- 检查USB连接和驱动安装
|
||||
- 确认选择了正确的设备型号
|
||||
- 验证生成的比特流文件是否有效
|
||||
- 重新安装FPGA开发工具
|
||||
|
||||
## 高级技巧
|
||||
|
||||
### 层次化设计
|
||||
|
||||
对于复杂项目,建议按功能模块划分设计层次,创建子模块后再进行顶层连接。
|
||||
|
||||
### 自定义组件
|
||||
|
||||
可以将常用电路打包为自定义组件,方便在不同项目中重复使用。
|
||||
|
||||
### 版本控制
|
||||
|
||||
定期保存设计快照,利用版本历史功能跟踪设计变更。
|
||||
|
||||
### 性能优化
|
||||
|
||||
- 减少关键路径上的组件数量
|
||||
- 优化时钟树结构
|
||||
- 合理布局减小互连延迟
|
||||
- 使用流水线结构提高吞吐量
|
||||
|
||||
## 联系我们
|
||||
|
||||
如有任何问题或建议,请通过以下方式联系我们:
|
||||
- 邮箱:support@fpgaweblab.com
|
||||
- 论坛:forum.fpgaweblab.com
|
||||
- 微信公众号:FPGA_WebLab
|
||||
|
||||
感谢您使用FPGA WebLab!
|
||||
`;
|
||||
|
||||
// 更新文档内容
|
||||
documentContent.value = mockDocContent;
|
||||
}
|
||||
|
||||
// --- 元器件管理 ---
|
||||
const showComponentsMenu = ref(false);
|
||||
const diagramData = ref<DiagramData>({
|
||||
|
@ -730,4 +892,27 @@ body {
|
|||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 文档面板样式 */
|
||||
.doc-panel {
|
||||
padding: 1.5rem;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 文档切换按钮样式 */
|
||||
.doc-toggle-btn {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
/* Markdown渲染样式调整 */
|
||||
:deep(.markdown-content) {
|
||||
padding: 1rem;
|
||||
background-color: hsl(var(--b1));
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue