This commit is contained in:
@@ -134,7 +134,7 @@ function closePanel() {
|
||||
<n-collapse v-if="formattedOutput" :default-expanded-names="['output']">
|
||||
<n-collapse-item :title="`输出 (${outputLines} 行)`" name="output">
|
||||
<template #header>
|
||||
<n-flex align="center" :gap="8">
|
||||
<n-flex align="center">
|
||||
<n-icon>
|
||||
<Icon icon="mdi:console" :width="14" :height="14" />
|
||||
</n-icon>
|
||||
@@ -170,7 +170,7 @@ function closePanel() {
|
||||
font-size: 12px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
max-height: 200px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,88 @@
|
||||
<script lang="ts" setup>
|
||||
// Vue 核心
|
||||
import { ref, computed, watch } from "vue"
|
||||
|
||||
// 第三方库
|
||||
import copyTextToClipboard from "copy-text-to-clipboard"
|
||||
import { useMessage } from "naive-ui"
|
||||
import { Icon } from "@iconify/vue"
|
||||
import { useIntervalFn } from "@vueuse/core"
|
||||
|
||||
// 组件
|
||||
import DebugEditor from "../components/DebugEditor.vue"
|
||||
import FloatingPanel from "../components/FloatingPanel.vue"
|
||||
import { code, input, reset, size, output } from "../composables/code"
|
||||
import { debug } from "../api"
|
||||
import { ref, computed } from "vue"
|
||||
import { Icon } from "@iconify/vue"
|
||||
|
||||
// 组合式函数和类型
|
||||
import { code, input, reset, size, output, status } from "../composables/code"
|
||||
import { Status } from "../types"
|
||||
import { debug } from "../api"
|
||||
|
||||
// ==================== 响应式状态 ====================
|
||||
const message = useMessage()
|
||||
|
||||
// 调试状态
|
||||
const showDebug = ref(false)
|
||||
const debugData = ref<any>(null)
|
||||
const currentStep = ref(0)
|
||||
const showFloatingPanel = ref(false)
|
||||
const isAutoRunning = ref(false)
|
||||
const autoRunInterval = ref<number | null>(null)
|
||||
|
||||
// 计算当前行和下一步行
|
||||
// UI 状态
|
||||
const showFloatingPanel = ref(false)
|
||||
|
||||
// 自动运行状态
|
||||
const isAutoRunning = ref(false)
|
||||
|
||||
const {
|
||||
pause: pauseAutoRun,
|
||||
resume: resumeAutoRun,
|
||||
isActive: isAutoRunActive,
|
||||
} = useIntervalFn(
|
||||
() => {
|
||||
if (currentStep.value < debugData.value.trace.length - 1) {
|
||||
currentStep.value++
|
||||
|
||||
// 如果遇到输入步骤,暂停自动运行
|
||||
if (debugData.value.trace[currentStep.value]?.event === "raw_input") {
|
||||
pauseAutoRun()
|
||||
isAutoRunning.value = false
|
||||
message.info("程序正在等待输入,自动运行已暂停")
|
||||
}
|
||||
} else {
|
||||
// 到达最后一步,停止自动运行
|
||||
pauseAutoRun()
|
||||
isAutoRunning.value = false
|
||||
}
|
||||
},
|
||||
500,
|
||||
{ immediate: false },
|
||||
)
|
||||
|
||||
// 调试状态快照(用于检测代码/输入变化)
|
||||
let debugStartCode = ""
|
||||
let debugStartInput = ""
|
||||
|
||||
// ==================== 监听器 ====================
|
||||
// 监听代码变化
|
||||
watch(
|
||||
() => code.value,
|
||||
(newCode) => {
|
||||
if (showDebug.value && newCode !== debugStartCode) {
|
||||
message.warning("代码已修改,请重新点击调试按钮")
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// 监听输入变化
|
||||
watch(
|
||||
() => input.value,
|
||||
(newInput) => {
|
||||
if (showDebug.value && newInput !== debugStartInput) {
|
||||
message.warning("输入已修改,请重新点击调试按钮")
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
// ==================== 计算属性 ====================
|
||||
// 调试行号相关
|
||||
const currentLine = computed(() => {
|
||||
if (
|
||||
debugData.value &&
|
||||
@@ -25,7 +91,6 @@ const currentLine = computed(() => {
|
||||
) {
|
||||
const line = debugData.value.trace[currentStep.value].line
|
||||
console.log(`Step ${currentStep.value}: currentLine = ${line}`)
|
||||
// 确保行号有效
|
||||
return line && line > 0 ? line : undefined
|
||||
}
|
||||
return undefined
|
||||
@@ -39,14 +104,13 @@ const nextLine = computed(() => {
|
||||
) {
|
||||
const line = debugData.value.trace[currentStep.value + 1].line
|
||||
console.log(`Step ${currentStep.value}: nextLine = ${line}`)
|
||||
// 确保行号有效且与当前行不同
|
||||
return line && line > 0 && line !== currentLine.value ? line : undefined
|
||||
}
|
||||
console.log(`Step ${currentStep.value}: nextLine = undefined (no next step)`)
|
||||
return undefined
|
||||
})
|
||||
|
||||
// 计算当前步骤的变量
|
||||
// 调试信息相关
|
||||
const currentVariables = computed(() => {
|
||||
if (
|
||||
debugData.value &&
|
||||
@@ -58,7 +122,6 @@ const currentVariables = computed(() => {
|
||||
return {}
|
||||
})
|
||||
|
||||
// 计算当前行文字
|
||||
const currentLineText = computed(() => {
|
||||
if (
|
||||
debugData.value &&
|
||||
@@ -76,7 +139,6 @@ const currentLineText = computed(() => {
|
||||
return undefined
|
||||
})
|
||||
|
||||
// 计算下一步行文字
|
||||
const nextLineText = computed(() => {
|
||||
if (
|
||||
debugData.value &&
|
||||
@@ -93,20 +155,10 @@ const nextLineText = computed(() => {
|
||||
return undefined
|
||||
})
|
||||
|
||||
// 检测是否为最后一步且事件为 raw_input
|
||||
const isLastStepRawInput = computed(() => {
|
||||
if (
|
||||
debugData.value &&
|
||||
debugData.value.trace &&
|
||||
debugData.value.trace.length > 0
|
||||
) {
|
||||
const lastStep = debugData.value.trace[debugData.value.trace.length - 1]
|
||||
return lastStep.event === "raw_input"
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// 获取事件类型的中文描述
|
||||
// ==================== 工具函数 ====================
|
||||
/**
|
||||
* 获取事件类型的中文描述
|
||||
*/
|
||||
function getEventText(event: string): string {
|
||||
switch (event) {
|
||||
case "step_line":
|
||||
@@ -114,11 +166,11 @@ function getEventText(event: string): string {
|
||||
case "call":
|
||||
return "(调用函数)"
|
||||
case "return":
|
||||
return "(函数返回)" // 更准确地表示函数返回
|
||||
return "(函数返回)"
|
||||
case "exception":
|
||||
return "(异常)"
|
||||
case "uncaught_exception":
|
||||
return "(未捕获异常)"
|
||||
return "(异常)"
|
||||
case "raw_input":
|
||||
return "(等待输入)"
|
||||
default:
|
||||
@@ -126,25 +178,61 @@ function getEventText(event: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
// 计算当前步骤的输出
|
||||
// 输出相关
|
||||
const currentOutput = computed(() => {
|
||||
if (
|
||||
debugData.value &&
|
||||
debugData.value.trace &&
|
||||
debugData.value.trace[currentStep.value]
|
||||
debugData.value.trace.length > 0
|
||||
) {
|
||||
return debugData.value.trace[currentStep.value].stdout || ""
|
||||
let outputText = ""
|
||||
|
||||
for (let i = 0; i <= currentStep.value; i++) {
|
||||
const step = debugData.value.trace[i]
|
||||
if (step) {
|
||||
if (step.event === "exception" || step.event === "uncaught_exception") {
|
||||
if (step.exception_msg) {
|
||||
outputText = step.exception_msg
|
||||
}
|
||||
} else if (step.stdout) {
|
||||
outputText = step.stdout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outputText = outputText.trimEnd()
|
||||
|
||||
const hasException = debugData.value.trace.some(
|
||||
(step: any) =>
|
||||
step.event === "exception" || step.event === "uncaught_exception",
|
||||
)
|
||||
status.value = hasException ? Status.RuntimeError : Status.Accepted
|
||||
|
||||
output.value = outputText
|
||||
return outputText
|
||||
}
|
||||
return output.value || ""
|
||||
})
|
||||
|
||||
// ==================== 主要功能函数 ====================
|
||||
/**
|
||||
* 复制代码到剪贴板
|
||||
*/
|
||||
function copy() {
|
||||
copyTextToClipboard(code.value)
|
||||
message.success("已经复制好了")
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始调试
|
||||
*/
|
||||
async function handleDebug() {
|
||||
showDebug.value = true
|
||||
showFloatingPanel.value = true
|
||||
// 保存调试开始时的代码和输入状态
|
||||
debugStartCode = code.value
|
||||
debugStartInput = input.value
|
||||
|
||||
const inputs = input.value ? input.value.split("\n") : []
|
||||
const res = await debug(code.value, inputs)
|
||||
debugData.value = res.data
|
||||
@@ -172,18 +260,28 @@ async function handleDebug() {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 调试控制函数 ====================
|
||||
/**
|
||||
* 跳转到第一步
|
||||
*/
|
||||
function firstStep() {
|
||||
if (debugData.value && debugData.value.trace) {
|
||||
currentStep.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上一步
|
||||
*/
|
||||
function prevStep() {
|
||||
if (debugData.value && debugData.value.trace && currentStep.value > 0) {
|
||||
currentStep.value--
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下一步
|
||||
*/
|
||||
function nextStep() {
|
||||
if (
|
||||
debugData.value &&
|
||||
@@ -194,6 +292,9 @@ function nextStep() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到最后一步
|
||||
*/
|
||||
function lastStep() {
|
||||
if (debugData.value && debugData.value.trace) {
|
||||
currentStep.value = debugData.value.trace.length - 1
|
||||
@@ -206,57 +307,59 @@ function lastStep() {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== UI控制函数 ====================
|
||||
/**
|
||||
* 关闭浮动面板
|
||||
*/
|
||||
function closeFloatingPanel() {
|
||||
showFloatingPanel.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭调试模式
|
||||
*/
|
||||
function closeDebug() {
|
||||
showDebug.value = false
|
||||
showFloatingPanel.value = false
|
||||
debugData.value = null
|
||||
currentStep.value = 0
|
||||
|
||||
// 清除保存的调试状态
|
||||
debugStartCode = ""
|
||||
debugStartInput = ""
|
||||
|
||||
// 停止自动运行
|
||||
if (autoRunInterval.value) {
|
||||
clearInterval(autoRunInterval.value)
|
||||
autoRunInterval.value = null
|
||||
}
|
||||
pauseAutoRun()
|
||||
isAutoRunning.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动运行/暂停
|
||||
*/
|
||||
function autoRun() {
|
||||
if (!debugData.value || !debugData.value.trace) return
|
||||
|
||||
if (isAutoRunning.value) {
|
||||
if (isAutoRunActive.value) {
|
||||
// 停止自动运行
|
||||
if (autoRunInterval.value) {
|
||||
clearInterval(autoRunInterval.value)
|
||||
autoRunInterval.value = null
|
||||
}
|
||||
pauseAutoRun()
|
||||
isAutoRunning.value = false
|
||||
} else {
|
||||
// 开始自动运行
|
||||
isAutoRunning.value = true
|
||||
autoRunInterval.value = setInterval(() => {
|
||||
if (currentStep.value < debugData.value.trace.length - 1) {
|
||||
currentStep.value++
|
||||
} else {
|
||||
// 到达最后一步,停止自动运行
|
||||
if (autoRunInterval.value) {
|
||||
clearInterval(autoRunInterval.value)
|
||||
autoRunInterval.value = null
|
||||
}
|
||||
isAutoRunning.value = false
|
||||
}
|
||||
}, 500) // 每500毫秒执行一步
|
||||
resumeAutoRun()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 头部工具栏 -->
|
||||
<n-flex align="center" class="header">
|
||||
<Icon icon="streamline-emojis:lemon" :width="24" :height="24"></Icon>
|
||||
<!-- 标题和基础操作 -->
|
||||
<Icon icon="streamline-emojis:lemon" :width="24" :height="24" />
|
||||
<span class="title">代码区</span>
|
||||
|
||||
<n-button quaternary type="primary" @click="copy">复制</n-button>
|
||||
<n-button quaternary @click="reset">清空</n-button>
|
||||
<n-button
|
||||
quaternary
|
||||
type="error"
|
||||
@@ -265,17 +368,21 @@ function autoRun() {
|
||||
>
|
||||
调试
|
||||
</n-button>
|
||||
|
||||
<!-- 调试控制按钮 -->
|
||||
<template v-if="showDebug">
|
||||
<!-- 步骤控制 -->
|
||||
<n-tooltip>
|
||||
<template #trigger>
|
||||
<n-button text @click="firstStep">
|
||||
<template #icon>
|
||||
<Icon icon="tabler:player-skip-back" />
|
||||
<Icon icon="material-symbols:skip-previous" />
|
||||
</template>
|
||||
</n-button>
|
||||
</template>
|
||||
第一步
|
||||
</n-tooltip>
|
||||
|
||||
<n-tooltip>
|
||||
<template #trigger>
|
||||
<n-button text type="primary" @click="prevStep">
|
||||
@@ -286,17 +393,20 @@ function autoRun() {
|
||||
</template>
|
||||
上一步
|
||||
</n-tooltip>
|
||||
|
||||
<n-tooltip>
|
||||
<template #trigger>
|
||||
<n-button
|
||||
:type="isAutoRunning ? 'warning' : 'success'"
|
||||
:type="isAutoRunning ? 'warning' : 'info'"
|
||||
text
|
||||
@click="autoRun"
|
||||
>
|
||||
<template #icon>
|
||||
<Icon
|
||||
:icon="
|
||||
isAutoRunning ? 'tabler:player-pause' : 'tabler:player-play'
|
||||
isAutoRunning
|
||||
? 'material-symbols:pause'
|
||||
: 'material-symbols:play-arrow'
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
@@ -304,6 +414,7 @@ function autoRun() {
|
||||
</template>
|
||||
{{ isAutoRunning ? "暂停自动运行" : "开始自动运行" }}
|
||||
</n-tooltip>
|
||||
|
||||
<n-tooltip>
|
||||
<template #trigger>
|
||||
<n-button type="error" text @click="nextStep">
|
||||
@@ -314,41 +425,32 @@ function autoRun() {
|
||||
</template>
|
||||
下一步
|
||||
</n-tooltip>
|
||||
|
||||
<n-tooltip>
|
||||
<template #trigger>
|
||||
<n-button text @click="lastStep">
|
||||
<template #icon>
|
||||
<Icon icon="tabler:player-skip-forward" />
|
||||
<Icon icon="material-symbols:skip-next" />
|
||||
</template>
|
||||
</n-button>
|
||||
</template>
|
||||
最后一步
|
||||
</n-tooltip>
|
||||
<n-tooltip>
|
||||
<template #trigger>
|
||||
<n-button text type="error" @click="closeDebug">
|
||||
<template #icon>
|
||||
<Icon icon="tabler:x" />
|
||||
</template>
|
||||
</n-button>
|
||||
</template>
|
||||
关闭调试
|
||||
</n-tooltip>
|
||||
</template>
|
||||
<n-button quaternary type="primary" @click="copy">复制</n-button>
|
||||
<n-button quaternary @click="reset">清空</n-button>
|
||||
|
||||
<!-- 面板控制 -->
|
||||
<n-button
|
||||
v-if="showDebug"
|
||||
quaternary
|
||||
type="info"
|
||||
@click="showFloatingPanel = !showFloatingPanel"
|
||||
>
|
||||
{{ showFloatingPanel ? "隐藏面板" : "显示面板" }}
|
||||
</n-button>
|
||||
</template>
|
||||
|
||||
<!-- 调试进度条 -->
|
||||
<n-flex
|
||||
align="center"
|
||||
v-if="showDebug && debugData && debugData.trace"
|
||||
align="center"
|
||||
class="progress-section"
|
||||
>
|
||||
<n-slider
|
||||
@@ -363,9 +465,23 @@ function autoRun() {
|
||||
步骤: {{ currentStep + 1 }} / {{ debugData.trace.length }}
|
||||
</span>
|
||||
</n-flex>
|
||||
|
||||
<!-- 关闭调试 -->
|
||||
<n-tooltip v-if="showDebug">
|
||||
<template #trigger>
|
||||
<n-button text type="error" @click="closeDebug">
|
||||
<template #icon>
|
||||
<Icon icon="tabler:x" />
|
||||
</template>
|
||||
</n-button>
|
||||
</template>
|
||||
关闭调试
|
||||
</n-tooltip>
|
||||
</n-flex>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<div class="debug-container">
|
||||
<!-- 代码编辑器 -->
|
||||
<DebugEditor
|
||||
v-model="code.value"
|
||||
:font-size="size"
|
||||
@@ -376,7 +492,7 @@ function autoRun() {
|
||||
:next-line-text="nextLineText"
|
||||
/>
|
||||
|
||||
<!-- 浮动面板 -->
|
||||
<!-- 浮动调试面板 -->
|
||||
<FloatingPanel
|
||||
:visible="showFloatingPanel"
|
||||
:variables="currentVariables"
|
||||
@@ -386,21 +502,25 @@ function autoRun() {
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
/* ==================== 头部样式 ==================== */
|
||||
.header {
|
||||
padding: 12px 20px;
|
||||
height: 60px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* ==================== 主容器样式 ==================== */
|
||||
.debug-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* ==================== 进度条样式 ==================== */
|
||||
.progress-section {
|
||||
flex: 1;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.debug-progress {
|
||||
|
||||
Reference in New Issue
Block a user