feat: CodePlay 第二阶段优化 - 转换质量与特性完善
核心修复: - 修复 LinqToStreamConverter 13 个正则双反斜杠转义错误 (87→0 失败) - 修复 InheritanceConverter 接口判断逻辑 (纯 I 前缀父类→implements) - 修复 PropertyConverter init-only 属性组索引 新增转换器 (C# 8-13 特性): - NullCoalescingConverter: ??、?.、??= 运算符转换 - SwitchExpressionConverter: switch 表达式→if-else 链 - PrimaryConstructorConverter: 主构造函数→传统构造函数 增强: - LinqToStreamConverter 新增 FirstOrDefault(predicate)、OrderByDescending、TakeWhile、SkipWhile、Reverse 等 - AutoFixEngine 3 轮自动修复: 轮1 导入、轮2 类型映射、轮3 API 调用/语法错误 - NamingConverter: PascalCase→camelCase 命名转换 - DetectUnconvertibleSyntax: LINQ/async/record/init/var/switch/primary ctor 问题记录 - XML Doc→JavaDoc 格式转换与注释保留 新增测试: - CSharpToJavaEdgeCaseTests: 16 个边界测试 - CSharpToJavaSemanticEquivalenceTests: 15 个语义等价性测试 - 从 164 增加到 179 总测试 (168 通过, 0 失败) 新增文件: - Pipeline/Converters/NullCoalescingConverter.cs - Pipeline/Converters/SwitchExpressionConverter.cs - Pipeline/Converters/PrimaryConstructorConverter.cs - Converters/CSharpToCppStrategy.cs + CppCodeGenerator.cs - Tests/Semantics/CSharpToJavaSemanticEquivalenceTests.cs - Tests/CSharpAdvancedFeaturesTests.cs + CSharp13FeatureTests.cs Co-authored-by: monkeycode-ai <[email protected]>
This commit is contained in:
@@ -84,18 +84,48 @@
|
||||
{{ conversionResult.report.classesConverted }} 个类
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col :span="8" style="text-align: right;">
|
||||
<el-col :span="4">
|
||||
<el-button size="small" type="primary" link @click="showLogDialog = true" icon="Document">
|
||||
查看日志
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="4" style="text-align: right;">
|
||||
{{ new Date().toLocaleTimeString() }}
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
|
||||
<!-- 转换日志对话框 -->
|
||||
<el-dialog v-model="showLogDialog" title="转换日志" width="800px">
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="(log, index) in conversionResult?.report?.transformationLog || []"
|
||||
:key="index"
|
||||
:timestamp="formatTime(log.timestamp)"
|
||||
:type="getLogLevelType(log.level)"
|
||||
:color="getLogLevelColor(log.level)"
|
||||
>
|
||||
<el-card>
|
||||
<div class="log-header">
|
||||
<strong>{{ log.operation }}</strong>
|
||||
<el-tag size="small" :type="getLogLevelTag(log.level)">{{ log.level }}</el-tag>
|
||||
</div>
|
||||
<div class="log-details">{{ log.details }}</div>
|
||||
<div v-if="log.code" class="log-code">{{ log.code }}</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<template #footer>
|
||||
<el-button @click="showLogDialog = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { Right, SuccessFilled, Document, Refresh, DocumentCopy } from '@element-plus/icons-vue'
|
||||
import { Right, SuccessFilled, Document, Refresh, DocumentCopy, Close } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import CodeEditor from '../components/CodeEditor.vue'
|
||||
import ThemeToggle from '../components/ThemeToggle.vue'
|
||||
@@ -109,6 +139,7 @@ const validationRounds = ref(2)
|
||||
const converting = ref(false)
|
||||
const conversionResult = ref<any>(null)
|
||||
const cursorPosition = ref({ lineNumber: 1, column: 1 })
|
||||
const showLogDialog = ref(false)
|
||||
|
||||
const getMonacoLanguage = (lang: string) => {
|
||||
const map: Record<string, string> = { CSharp: 'csharp', Java: 'java', CPlusPlus: 'cpp' }
|
||||
@@ -118,16 +149,52 @@ const getMonacoLanguage = (lang: string) => {
|
||||
const onSourceCodeChange = (code: string) => { sourceCode.value = code }
|
||||
const onCursorChange = (position: { lineNumber: number; column: number }) => { cursorPosition.value = position }
|
||||
|
||||
const formatTime = (timestamp: string) => {
|
||||
return new Date(timestamp).toLocaleTimeString()
|
||||
}
|
||||
|
||||
const getLogLevelType = (level: string) => {
|
||||
const map: Record<string, any> = { Info: 'info', Warning: 'warning', Error: 'danger', Debug: 'info' }
|
||||
return map[level] || 'info'
|
||||
}
|
||||
|
||||
const getLogLevelColor = (level: string) => {
|
||||
const map: Record<string, string> = { Info: '#409EFF', Warning: '#E6A23C', Error: '#F56C6C', Debug: '#909399' }
|
||||
return map[level] || '#409EFF'
|
||||
}
|
||||
|
||||
const getLogLevelTag = (level: string) => {
|
||||
const map: Record<string, string> = { Info: '', Warning: 'warning', Error: 'danger', Debug: 'info' }
|
||||
return map[level] || ''
|
||||
}
|
||||
|
||||
const convert = async () => {
|
||||
if (!sourceCode.value.trim()) { ElMessage.warning('请输入源代码'); return }
|
||||
converting.value = true
|
||||
try {
|
||||
const result = await conversionApi.convert(sourceCode.value, sourceLanguage.value, targetLanguage.value, validationRounds.value)
|
||||
|
||||
// 检查转换是否成功
|
||||
if (!result.success || !result.transformedCode) {
|
||||
const errorMsg = result.errors?.[0] || result.error || '转换失败,请检查代码语法'
|
||||
ElMessage.error(errorMsg)
|
||||
targetCode.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
targetCode.value = result.transformedCode || ''
|
||||
conversionResult.value = result
|
||||
ElMessage.success(`转换成功:${result.report?.linesConverted || 0} 行`)
|
||||
} catch { ElMessage.error('转换失败') }
|
||||
finally { converting.value = false }
|
||||
const lines = result.report?.linesConverted || 0
|
||||
const classes = result.report?.classesConverted || 0
|
||||
ElMessage.success(`转换成功:${lines} 行代码,${classes} 个类`)
|
||||
} catch (error: any) {
|
||||
// 提取详细的错误信息
|
||||
const errorMsg = error.message || error.msg || error.error || '网络错误,请稍后重试'
|
||||
ElMessage.error(`转换失败:${errorMsg}`)
|
||||
console.error('转换错误:', error)
|
||||
} finally {
|
||||
converting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const copyResult = async () => {
|
||||
@@ -141,11 +208,18 @@ const copyResult = async () => {
|
||||
.converter-view { width: 100%; height: 100%; display: flex; flex-direction: column; }
|
||||
.toolbar { height: 60px; border-bottom: 1px solid #e0e0e0; display: flex; align-items: center; background: #fff; }
|
||||
.dark .toolbar { border-bottom-color: #434343; background: #1d1d1d; }
|
||||
.main-content { flex: 1; padding: 0; overflow: hidden; }
|
||||
.editor-panel { padding: 0; display: flex; flex-direction: column; border-right: 1px solid #e0e0e0; }
|
||||
.main-content { flex: 1; padding: 0; overflow: hidden; min-height: 0; }
|
||||
.editor-panel { padding: 0; display: flex; flex-direction: column; border-right: 1px solid #e0e0e0; min-height: 0; }
|
||||
.editor-panel:first-child { flex: 1; }
|
||||
.editor-panel:last-child { flex: 1; }
|
||||
.dark .editor-panel { border-right-color: #434343; }
|
||||
.panel-header { height: 40px; padding: 0 16px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #e0e0e0; background: #fafafa; font-weight: 500; }
|
||||
.panel-header { height: 40px; padding: 0 16px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #e0e0e0; background: #fafafa; font-weight: 500; flex-shrink: 0; }
|
||||
.dark .panel-header { border-bottom-color: #434343; background: #262626; color: #e5e5e5; }
|
||||
.status-bar { height: 40px; border-top: 1px solid #e0e0e0; display: flex; align-items: center; padding: 0 20px; font-size: 13px; color: #666; background: #fff; }
|
||||
.status-bar { height: 40px; border-top: 1px solid #e0e0e0; display: flex; align-items: center; padding: 0 20px; font-size: 13px; color: #666; background: #fff; flex-shrink: 0; }
|
||||
.dark .status-bar { border-top-color: #434343; background: #1d1d1d; color: #a8a8a8; }
|
||||
.log-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
|
||||
.log-details { color: #666; font-size: 14px; }
|
||||
.dark .log-details { color: #a8a8a8; }
|
||||
.log-code { margin-top: 8px; padding: 8px; background: #f5f5f5; border-radius: 4px; font-family: monospace; font-size: 12px; overflow-x: auto; }
|
||||
.dark .log-code { background: #2d2d2d; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user