add: home select exp
This commit is contained in:
@@ -1,14 +1,9 @@
|
||||
<template>
|
||||
<div class="bg-base-200 min-h-screen">
|
||||
<main class="hero min-h-screen bg-base-200">
|
||||
<div class="hero-content flex-col lg:flex-row-reverse gap-8 lg:gap-12 py-10 px-4">
|
||||
<!-- 图片容器 -->
|
||||
<div
|
||||
class="image-container relative w-full max-w-sm hover:scale-105 hover:-rotate-1 transition-transform duration-500 ease-in-out">
|
||||
<img src="https://placehold.co/600x400"
|
||||
class="w-full rounded-2xl shadow-2xl border-4 border-base-300 transition-shadow duration-300 hover:shadow-primary" />
|
||||
<!-- 这里使用relative定位,限制覆盖层只在图片容器内 -->
|
||||
<div class="absolute inset-0 bg-primary opacity-10 rounded-2xl pointer-events-none"></div>
|
||||
<div class="hero-content flex-col xl:flex-row-reverse gap-8 xl:gap-12 py-10 px-4"> <!-- 例程轮播容器 -->
|
||||
<div class="w-full flex justify-center" style="min-width: 650px;">
|
||||
<TutorialCarousel :autoRotationInterval="3000" />
|
||||
</div>
|
||||
<!-- 内容容器 -->
|
||||
<div class="content-container max-w-md lg:max-w-2xl transform transition-all duration-500 ease-in-out">
|
||||
@@ -93,6 +88,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import "@/router";
|
||||
import TutorialCarousel from "@/components/TutorialCarousel.vue";
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss">
|
||||
|
||||
445
src/views/MarkdownTestView.vue
Normal file
445
src/views/MarkdownTestView.vue
Normal file
@@ -0,0 +1,445 @@
|
||||
<template>
|
||||
<div class="p-8 max-w-5xl mx-auto">
|
||||
<h1 class="text-2xl font-bold mb-6">Markdown 渲染器语法高亮测试</h1>
|
||||
|
||||
<div class="grid grid-cols-1 gap-8">
|
||||
<!-- 测试控制面板 -->
|
||||
<div class="bg-base-200 p-4 rounded-lg">
|
||||
<div class="flex gap-4 mb-4">
|
||||
<button @click="currentTheme = 'light'" class="btn btn-primary" :class="{'btn-outline': currentTheme !== 'light'}">
|
||||
亮色主题
|
||||
</button>
|
||||
<button @click="currentTheme = 'dark'" class="btn btn-primary" :class="{'btn-outline': currentTheme !== 'dark'}">
|
||||
暗色主题
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 示例展示 -->
|
||||
<div class="bg-base-100 rounded-lg shadow-lg overflow-hidden" :data-theme="currentTheme">
|
||||
<div class="p-4 bg-base-200 font-semibold">Markdown 渲染结果</div>
|
||||
<div class="px-1">
|
||||
<MarkdownRenderer :content="sampleContent" :remove-first-h1="false" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import MarkdownRenderer from '@/components/MarkdownRenderer.vue';
|
||||
|
||||
const currentTheme = ref('light');
|
||||
|
||||
// 包含各种代码示例的 Markdown 示例内容
|
||||
const sampleContent = ref(`
|
||||
# Markdown 语法高亮测试
|
||||
|
||||
这是一个用于测试 Markdown 渲染器语法高亮功能的页面。下面是一些代码示例。
|
||||
|
||||
## JavaScript 代码示例
|
||||
|
||||
\`\`\`javascript
|
||||
// 一个简单的 JavaScript 函数
|
||||
function calculateSum(a, b) {
|
||||
// 这是一个注释
|
||||
const sum = a + b;
|
||||
console.log(\`计算结果: \${sum}\`);
|
||||
return sum;
|
||||
}
|
||||
|
||||
// 类示例
|
||||
class Person {
|
||||
constructor(name, age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
greet() {
|
||||
return \`你好,我是 \${this.name},我 \${this.age} 岁了。\`;
|
||||
}
|
||||
}
|
||||
|
||||
// 使用 async/await
|
||||
async function fetchData() {
|
||||
try {
|
||||
const response = await fetch('https://api.example.com/data');
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('获取数据出错:', error);
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## TypeScript 代码示例
|
||||
|
||||
\`\`\`typescript
|
||||
// TypeScript 接口
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
age?: number;
|
||||
}
|
||||
|
||||
// 泛型函数
|
||||
function getFirst<T>(array: T[]): T | undefined {
|
||||
return array.length > 0 ? array[0] : undefined;
|
||||
}
|
||||
|
||||
// 类型别名和联合类型
|
||||
type Result<T> =
|
||||
| { success: true; value: T }
|
||||
| { success: false; error: Error };
|
||||
|
||||
// 装饰器
|
||||
function log(target: any, key: string) {
|
||||
const originalMethod = target[key];
|
||||
|
||||
target[key] = function(...args: any[]) {
|
||||
console.log(\`调用方法 \${key} 参数:, args);
|
||||
return originalMethod.apply(this, args);
|
||||
};
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
class Calculator {
|
||||
@log
|
||||
add(a: number, b: number): number {
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## HTML & CSS 示例
|
||||
|
||||
\`\`\`html
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>示例页面</title>
|
||||
<style>
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background-color: #4a6cf7;
|
||||
color: white;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h2>欢迎使用我们的应用</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>这是卡片内容区域。</p>
|
||||
<button onclick="alert('你点击了按钮!')">点击我</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
\`\`\`
|
||||
|
||||
## C# 代码示例
|
||||
|
||||
\`\`\`csharp
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Demo
|
||||
{
|
||||
// 简单的用户类
|
||||
public class User
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"User(Id={Id}, Name={Name}, Email={Email})";
|
||||
}
|
||||
}
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static async Task<List<User>> GetUsersAsync()
|
||||
{
|
||||
// 模拟异步操作
|
||||
await Task.Delay(1000);
|
||||
|
||||
return new List<User>
|
||||
{
|
||||
new User { Id = 1, Name = "张三", Email = "zhangsan@example.com" },
|
||||
new User { Id = 2, Name = "李四", Email = "lisi@example.com" },
|
||||
new User { Id = 3, Name = "王五", Email = "wangwu@example.com" }
|
||||
};
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("获取用户列表中...");
|
||||
|
||||
var users = GetUsersAsync().GetAwaiter().GetResult();
|
||||
|
||||
foreach (var user in users)
|
||||
{
|
||||
Console.WriteLine(user);
|
||||
}
|
||||
|
||||
Console.WriteLine("完成!");
|
||||
}
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## Python 代码示例
|
||||
|
||||
\`\`\`python
|
||||
import os
|
||||
import json
|
||||
from typing import List, Dict, Any, Optional
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Person:
|
||||
name: str
|
||||
age: int
|
||||
email: Optional[str] = None
|
||||
|
||||
def greet(self) -> str:
|
||||
return f"你好,我是 {self.name},我 {self.age} 岁了。"
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return {
|
||||
"name": self.name,
|
||||
"age": self.age,
|
||||
"email": self.email
|
||||
}
|
||||
|
||||
# 一些常用的 Python 函数
|
||||
def read_json_file(file_path: str) -> Dict[str, Any]:
|
||||
"""从JSON文件读取数据
|
||||
|
||||
Args:
|
||||
file_path: JSON文件路径
|
||||
|
||||
Returns:
|
||||
解析后的JSON数据
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: 如果文件不存在
|
||||
json.JSONDecodeError: 如果JSON格式不正确
|
||||
"""
|
||||
if not os.path.exists(file_path):
|
||||
raise FileNotFoundError(f"文件不存在: {file_path}")
|
||||
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
# 列表推导式与生成器
|
||||
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||
even_numbers = [n for n in numbers if n % 2 == 0]
|
||||
squared = (n**2 for n in even_numbers) # 生成器表达式
|
||||
|
||||
# 使用装饰器
|
||||
def log_function_call(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
print(f"调用函数: {func.__name__}")
|
||||
result = func(*args, **kwargs)
|
||||
print(f"函数返回: {result}")
|
||||
return result
|
||||
return wrapper
|
||||
|
||||
@log_function_call
|
||||
def add(a: int, b: int) -> int:
|
||||
return a + b
|
||||
|
||||
if __name__ == "__main__":
|
||||
person = Person("张三", 30, "zhangsan@example.com")
|
||||
print(person.greet())
|
||||
print(add(5, 3))
|
||||
\`\`\`
|
||||
|
||||
## VHDL 代码示例
|
||||
|
||||
\`\`\`vhdl
|
||||
library IEEE;
|
||||
use IEEE.STD_LOGIC_1164.ALL;
|
||||
use IEEE.NUMERIC_STD.ALL;
|
||||
|
||||
entity Counter is
|
||||
Port (
|
||||
clk : in STD_LOGIC;
|
||||
reset : in STD_LOGIC;
|
||||
enable : in STD_LOGIC;
|
||||
count : out STD_LOGIC_VECTOR(7 downto 0)
|
||||
);
|
||||
end Counter;
|
||||
|
||||
architecture Behavioral of Counter is
|
||||
signal count_reg : unsigned(7 downto 0) := (others => '0');
|
||||
begin
|
||||
process(clk, reset)
|
||||
begin
|
||||
if reset = '1' then
|
||||
count_reg <= (others => '0');
|
||||
elsif rising_edge(clk) then
|
||||
if enable = '1' then
|
||||
count_reg <= count_reg + 1;
|
||||
end if;
|
||||
end if;
|
||||
end process;
|
||||
|
||||
count <= STD_LOGIC_VECTOR(count_reg);
|
||||
end Behavioral;
|
||||
\`\`\`
|
||||
|
||||
## Verilog 代码示例
|
||||
|
||||
\`\`\`verilog
|
||||
module counter(
|
||||
input wire clk,
|
||||
input wire reset,
|
||||
input wire enable,
|
||||
output reg [7:0] count
|
||||
);
|
||||
|
||||
// 初始化计数器
|
||||
initial begin
|
||||
count = 8'b0;
|
||||
end
|
||||
|
||||
// 在时钟上升沿处理
|
||||
always @(posedge clk or posedge reset) begin
|
||||
if (reset) begin
|
||||
count <= 8'b0;
|
||||
end else if (enable) begin
|
||||
count <= count + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// 显示当前计数值
|
||||
always @(count) begin
|
||||
$display("当前计数值: %d", count);
|
||||
end
|
||||
endmodule
|
||||
\`\`\`
|
||||
|
||||
## JSON 示例
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"name": "fpga-weblab",
|
||||
"version": "1.0.0",
|
||||
"description": "FPGA WebLab 项目配置",
|
||||
"settings": {
|
||||
"theme": {
|
||||
"light": {
|
||||
"primary": "#4a6cf7",
|
||||
"secondary": "#f79e1b",
|
||||
"background": "#ffffff"
|
||||
},
|
||||
"dark": {
|
||||
"primary": "#6d8aff",
|
||||
"secondary": "#ffb74d",
|
||||
"background": "#121212"
|
||||
}
|
||||
},
|
||||
"features": [
|
||||
"代码高亮",
|
||||
"实时预览",
|
||||
"项目管理",
|
||||
"远程硬件访问"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.0",
|
||||
"marked": "^12.0.0",
|
||||
"highlight.js": "^11.9.0"
|
||||
}
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
## Shell 脚本示例
|
||||
|
||||
\`\`\`bash
|
||||
#!/bin/bash
|
||||
|
||||
# 定义变量
|
||||
PROJECT_DIR=$(pwd)
|
||||
OUTPUT_DIR="$PROJECT_DIR/build"
|
||||
LOG_FILE="$PROJECT_DIR/build.log"
|
||||
|
||||
# 创建输出目录
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# 定义函数
|
||||
function log_message() {
|
||||
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
|
||||
echo "[$timestamp] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
# 清理旧构建
|
||||
log_message "清理旧构建文件..."
|
||||
rm -rf "$OUTPUT_DIR/*"
|
||||
|
||||
# 执行构建
|
||||
log_message "开始构建项目..."
|
||||
npm run build
|
||||
|
||||
# 检查构建结果
|
||||
if [ $? -eq 0 ]; then
|
||||
log_message "构建成功!输出文件位于: $OUTPUT_DIR"
|
||||
else
|
||||
log_message "构建失败,请检查错误信息"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 统计文件数量
|
||||
file_count=$(find "$OUTPUT_DIR" -type f | wc -l)
|
||||
log_message "共构建了 $file_count 个文件"
|
||||
|
||||
# 显示环境信息
|
||||
echo "系统信息:"
|
||||
echo "----------------------"
|
||||
echo "操作系统: $(uname -s)"
|
||||
echo "Node 版本: $(node -v)"
|
||||
echo "NPM 版本: $(npm -v)"
|
||||
echo "磁盘空间: $(df -h | grep -E '^/dev')"
|
||||
\`\`\`
|
||||
|
||||
## 其他示例
|
||||
|
||||
这里是一个内联代码示例:\`const value = calculate(x, y);\`
|
||||
|
||||
`);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -26,9 +26,7 @@
|
||||
<div
|
||||
class="resizer bg-base-100 hover:bg-primary hover:opacity-70 active:bg-primary active:opacity-90 transition-colors"
|
||||
@mousedown="startResize"
|
||||
></div>
|
||||
|
||||
<!-- 右侧编辑区域 -->
|
||||
></div> <!-- 右侧编辑区域 -->
|
||||
<div
|
||||
class="bg-base-200 h-full overflow-hidden flex flex-col"
|
||||
:style="{ width: 100 - leftPanelWidth + '%' }"
|
||||
@@ -41,10 +39,9 @@
|
||||
:componentConfig="selectedComponentConfig"
|
||||
@updateProp="updateComponentProp"
|
||||
@updateDirectProp="updateComponentDirectProp"
|
||||
/>
|
||||
<div
|
||||
/> <div
|
||||
v-else
|
||||
class="doc-panel overflow-y-auto bg-base-100 rounded-md h-full"
|
||||
class="doc-panel overflow-y-auto h-full"
|
||||
>
|
||||
<MarkdownRenderer :content="documentContent" />
|
||||
</div>
|
||||
@@ -82,20 +79,57 @@ import {
|
||||
const showDocPanel = ref(false);
|
||||
const documentContent = ref("");
|
||||
|
||||
// 获取路由参数
|
||||
import { useRoute } from 'vue-router';
|
||||
const route = useRoute();
|
||||
|
||||
// 切换文档面板和属性面板
|
||||
async function toggleDocPanel() {
|
||||
showDocPanel.value = !showDocPanel.value;
|
||||
|
||||
// 如果切换到文档面板,则获取文档内容
|
||||
if (showDocPanel.value) {
|
||||
const response = await fetch("/doc/01_water_led/water_led.md");
|
||||
documentContent.value = (await response.text()).replace(
|
||||
/.\/images/gi,
|
||||
"/doc/01_water_led/images",
|
||||
);
|
||||
await loadDocumentContent();
|
||||
}
|
||||
}
|
||||
|
||||
// 加载文档内容
|
||||
async function loadDocumentContent() {
|
||||
try {
|
||||
// 从路由参数中获取教程ID
|
||||
const tutorialId = route.query.tutorial as string || '02'; // 默认加载02例程
|
||||
|
||||
// 构建文档路径
|
||||
let docPath = `/doc/${tutorialId}/doc.md`;
|
||||
|
||||
// 检查当前路径是否包含下划线(例如 02_key 格式)
|
||||
// 如果不包含,那么使用更新的命名格式
|
||||
if (!tutorialId.includes('_')) {
|
||||
docPath = `/doc/${tutorialId}/doc.md`;
|
||||
}
|
||||
|
||||
// 获取文档内容
|
||||
const response = await fetch(docPath);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load document: ${response.status}`);
|
||||
}
|
||||
|
||||
// 更新文档内容,并替换图片路径
|
||||
documentContent.value = (await response.text())
|
||||
.replace(/.\/images/gi, `/doc/${tutorialId}/images`);
|
||||
} catch (error) {
|
||||
console.error('加载文档失败:', error);
|
||||
documentContent.value = '# 文档加载失败\n\n无法加载请求的文档。'; }
|
||||
}
|
||||
|
||||
// 检查是否有例程参数,如果有则自动打开文档面板
|
||||
onMounted(async () => {
|
||||
if (route.query.tutorial) {
|
||||
showDocPanel.value = true;
|
||||
await loadDocumentContent();
|
||||
}
|
||||
});
|
||||
|
||||
// --- 元器件管理 ---
|
||||
const showComponentsMenu = ref(false);
|
||||
const diagramData = ref<DiagramData>({
|
||||
@@ -807,8 +841,10 @@ body {
|
||||
/* 文档面板样式 */
|
||||
.doc-panel {
|
||||
padding: 1.5rem;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
background-color: transparent; /* 使用透明背景 */
|
||||
border: none; /* 确保没有边框 */
|
||||
}
|
||||
|
||||
/* 文档切换按钮样式 */
|
||||
|
||||
Reference in New Issue
Block a user