diff --git a/package.json b/package.json index 6e130e4..38a07b5 100644 --- a/package.json +++ b/package.json @@ -9,29 +9,39 @@ "preview": "vite preview" }, "dependencies": { - "vue": "^3.4.0", - "vue-router": "^4.2.0", - "element-plus": "^2.5.0", + "@codemirror/basic-setup": "^0.20.0", + "@codemirror/commands": "^6.8.0", + "@codemirror/lang-cpp": "^6.0.2", + "@codemirror/lang-javascript": "^6.2.3", + "@codemirror/language": "^6.11.0", + "@codemirror/rangeset": "^0.19.9", + "@codemirror/state": "^6.5.2", + "@codemirror/theme-one-dark": "^6.1.2", + "@codemirror/view": "^6.36.4", "@element-plus/icons-vue": "^2.3.0", - "echarts": "^5.5.0", - "vue-echarts": "^6.6.0", - "three": "^0.160.0", - "file-saver": "^2.0.5", - "xlsx": "^0.18.5", + "@monaco-editor/loader": "^1.5.0", "animate.css": "^4.1.1", "axios": "^1.6.0", - "pinia": "^2.1.0", "dayjs": "^1.11.0", - - + "echarts": "^5.5.0", + "element-plus": "^2.5.0", + "file-saver": "^2.0.5", + "js-cookie": "^3.0.5", "monaco-editor": "^0.30.1", - "js-cookie": "^3.0.5" + "pinia": "^2.1.0", + "three": "^0.160.0", + "vue": "^3.4.0", + "vue-codemirror": "^6.1.1", + "vue-echarts": "^6.6.0", + "vue-monaco-editor": "^0.0.19", + "vue-router": "^4.2.0", + "xlsx": "^0.18.5" }, "devDependencies": { "@vitejs/plugin-vue": "^5.0.0", - "vite": "^5.0.0", "sass": "^1.70.0", "unplugin-auto-import": "^0.17.0", - "unplugin-vue-components": "^0.26.0" + "unplugin-vue-components": "^0.26.0", + "vite": "^5.0.0" } } diff --git a/src/App.vue b/src/App.vue index 0f8ec8d..a470e8d 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,10 +1,10 @@ @@ -18,11 +18,11 @@ import AppFooter from './components/AppFooter.vue' min-height: 100vh; display: flex; flex-direction: column; - + .main-content { flex: 1; margin-top: 60px; // 为固定的头部留出空间 padding: 20px; } } - \ No newline at end of file + diff --git a/src/components/CodeEditor/CodeEditor.vue b/src/components/CodeEditor/CodeEditor.vue new file mode 100644 index 0000000..801c624 --- /dev/null +++ b/src/components/CodeEditor/CodeEditor.vue @@ -0,0 +1,192 @@ + + + + + diff --git a/src/router/index.js b/src/router/index.js index 55f1d7e..8d62c73 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -67,7 +67,16 @@ const routes = [ path: '/exam/start/:id', component: () => import('../views/paper/exam/exam.vue'), name: 'StartExam', - meta: { title: '开始考试' }, + + meta: { + + title: '开始考试', + + isExam: true, + + hideNav: true + + }, hidden: true }, diff --git a/src/utils/request.js b/src/utils/request.js index 526591e..728bbd9 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -6,7 +6,7 @@ import { useUserStore } from '../stores/user' const service = axios.create({ // baseURL: import.meta.env.VITE_API_BASE_URL || '/api', // 从环境变量获取 API 基础 URL //baseURL: 'http://localhost:8101', // 从环境变量获取 API 基础 URL - baseURL: 'http://localhost:8084', // 从环境变量获取 API 基础 URL + baseURL: 'http://localhost:8101', // 从环境变量获取 API 基础 URL timeout: 15000, // 请求超时时间 }) @@ -23,7 +23,7 @@ service.interceptors.request.use( - config.headers['token'] = `eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NDE3ODAxNDMsInVzZXJuYW1lIjoiYWRtaW4ifQ._jKmdu1T-zpf_qSWRTBtovJ51v2ONC6CGF-60MLJOOE` + config.headers['token'] = `eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NDI0NzI5MzUsInVzZXJuYW1lIjoiYWRtaW4ifQ.-4e5HlHyM9gYlfkaraNdIcPDAeegiPoe3KPRaID9fRY` // 根据请求方法和数据类型动态设置 Content-Type diff --git a/src/views/mall/Mall.vue b/src/views/mall/Mall.vue index 16a441f..315f569 100644 --- a/src/views/mall/Mall.vue +++ b/src/views/mall/Mall.vue @@ -1,16 +1,410 @@ - - \ No newline at end of file + +.challenge-header { + margin-bottom: 24px; + padding-bottom: 20px; + border-bottom: 1px solid #eef2f7; +} + +.title-section { + display: flex; + align-items: center; + gap: 16px; + margin-bottom: 16px; +} + +.challenge-header h2 { + margin: 0; + color: #1a202c; + font-size: 26px; + font-weight: 600; +} + +.difficulty { + padding: 4px 12px; + border-radius: 20px; + font-size: 13px; + font-weight: 500; +} + +.difficulty.easy { + background: #e6f6e6; + color: #2f9e44; +} + +.challenge-info { + display: flex; + gap: 12px; +} + +.info-tag { + display: inline-flex; + align-items: center; + padding: 8px 16px; + background: #f8fafc; + border-radius: 8px; + font-size: 14px; + color: #64748b; + transition: all 0.2s; +} + +.info-tag:hover { + background: #f1f5f9; + transform: translateY(-1px); +} + +.info-tag i { + margin-right: 8px; + color: #3b82f6; +} + +.challenge-content { + display: grid; + grid-template-columns: 320px 1fr; + gap: 24px; +} + +.section-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 16px; +} + +.section-header h3 { + margin: 0; + font-size: 18px; + color: #1a202c; + display: flex; + align-items: center; + gap: 8px; +} + +.section-header h3 i { + color: #3b82f6; +} + +.case-count { + font-size: 14px; + color: #64748b; + background: #f1f5f9; + padding: 4px 12px; + border-radius: 16px; +} + +.test-cases { + display: flex; + flex-direction: column; + gap: 16px; +} + +.test-case-card { + border: 1px solid #e2e8f0; + border-radius: 10px; + overflow: hidden; + transition: all 0.2s; +} + +.test-case-card:hover { + border-color: #cbd5e1; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); +} + +.test-case-header { + padding: 12px 16px; + background: #f8fafc; + display: flex; + justify-content: space-between; + align-items: center; +} + +.case-number { + font-weight: 500; + color: #64748b; +} + +.case-status { + font-size: 14px; +} + +.case-status.success i { + color: #2f9e44; +} + +.test-case-content { + padding: 16px; +} + +.test-input, .test-output { + margin: 8px 0; +} + +.label { + font-weight: 500; + color: #475569; + margin-bottom: 4px; +} + +code { + display: block; + background: #f8fafc; + padding: 8px 12px; + border-radius: 6px; + font-family: 'Fira Code', monospace; + font-size: 13px; + color: #334155; + border: 1px solid #e2e8f0; +} + +.editor-section { + display: flex; + flex-direction: column; + gap: 16px; +} + +.editor-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 0 8px 0; +} + +.editor-tabs { + display: flex; + gap: 2px; +} + +.tab { + padding: 8px 16px; + font-size: 14px; + color: #64748b; + border-radius: 6px; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; +} + +.tab.active { + background: #f1f5f9; + color: #3b82f6; + font-weight: 500; +} + +.editor-actions { + display: flex; + gap: 12px; +} + +.action-btn { + padding: 8px 16px; + border: 1px solid #e2e8f0; + background: white; + border-radius: 6px; + color: #64748b; + font-size: 14px; + cursor: pointer; + display: flex; + align-items: center; + gap: 6px; + transition: all 0.2s; +} + +.action-btn:hover { + border-color: #cbd5e1; + background: #f8fafc; +} + +.submit-btn { + padding: 8px 20px; + background: #3b82f6; + color: white; + border: none; + border-radius: 6px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + transition: all 0.2s; +} + +.submit-btn:hover { + background: #2563eb; + transform: translateY(-1px); +} + +.code-editor-wrapper { + border-radius: 10px; + overflow: hidden; + border: 1px solid #e2e8f0; + flex-grow: 1; + height: 600px; +} + +.result-section { + grid-column: 1 / -1; + background: #f8fafc; + border-radius: 10px; + padding: 20px; +} + +.result-content { + margin: 0; + padding: 16px; + background: white; + border-radius: 8px; + border: 1px solid #e2e8f0; + font-family: 'Fira Code', monospace; + font-size: 13px; + line-height: 1.5; +} + +@media (max-width: 1024px) { + .challenge-content { + grid-template-columns: 1fr; + } + + .test-cases-section, .editor-section { + grid-column: 1; + } + + .info-tag { + padding: 6px 12px; + } +} + diff --git a/src/views/paper/exam/exam.vue b/src/views/paper/exam/exam.vue index 6b4fc4e..928d3ec 100644 --- a/src/views/paper/exam/exam.vue +++ b/src/views/paper/exam/exam.vue @@ -96,54 +96,104 @@ -
-
-

输入描述

-
- -

输出描述

-
- -

示例输入

-
{{ quData.sampleInput }}
- -

示例输出

-
{{ quData.sampleOutput }}
-
- -
-
- - - - +
+
+
+ {{ quData.sort + 1 }}. +

{{ quData.title || '编程题' }}

- - - -
- 运行代码 - 提交代码 +
+ + + 内存限制:{{ quData.memoryLimit || 256 }} MB + + + + 时间限制:{{ quData.timeLimit || 1000 }}ms +
-
-

运行结果

- +
+ +
+
+

题目描述

+
+
+ +
+

输入描述

+
+
+ +
+

输出描述

+
+
-
-

输出:

-
{{ runResult.output }}
+
+

示例

+
+
+
+
输入示例
+
{{ quData.sampleInput }}
+
+
+
输出示例
+
{{ quData.sampleOutput }}
+
+
+
+
-
-

预期输出:

-
{{ runResult.expectedOutput }}
+ +
+
+
+ + + + {{ lang.label }} + + +
+
+ + +
+
+ + + +
+
+ + 执行结果 +
+
{{ codeResult }}
+
@@ -174,11 +224,20 @@ import { paperDetail, quDetail, handExam, fillAnswer } from '@/api/paper/exam' import { ElLoading } from 'element-plus' import ExamTimer from '@/views/paper/exam/components/ExamTimer/index.vue' import { runCode, submitCode } from '@/api/qu/qu' -import CodeEditor from '@/components/CodeEditor/index.vue' +import { Codemirror } from 'vue-codemirror' +import { cpp } from '@codemirror/lang-cpp' +import { oneDark } from '@codemirror/theme-one-dark' +import { EditorView, gutter, GutterMarker } from '@codemirror/view' +import { StateField, StateEffect } from '@codemirror/state' +import CodeEditor from '@/components/CodeEditor/CodeEditor.vue' export default { name: 'ExamProcess', - components: { ExamTimer, CodeEditor }, + components: { + ExamTimer, + Codemirror, + CodeEditor + }, data() { return { // 全屏/不全屏 @@ -212,10 +271,22 @@ export default { // 已答ID answeredIds: [], language: 'cpp', - code: '// 请在此编写代码\n#include \n\nint main() {\n // 在此编写代码\n return 0;\n}', - runLoading: false, + code: '', + showEditor: true, + extensions: null, + defaultCode: { + cpp: '#include \nusing namespace std;\n\nint main() {\n // 在此编写代码\n return 0;\n}', + c: '#include \n\nint main() {\n // 在此编写代码\n return 0;\n}' + }, submitLoading: false, - runResult: null + breakpoints: new Set(), + editor: null, + currentQuestion: null, + codeResult: null, + languages: [ + { label: 'C++', value: 'cpp' }, + { label: 'C', value: 'c' }, + ] } }, created() { @@ -377,47 +448,41 @@ export default { }) }, - // 试卷详情 - fetchQuData(item) { - // 打开 + // 获取题目详情 + async fetchQuData(item) { + if (!item) return + const loading = ElLoading.service({ - text: '拼命加载中', + text: '加载中...', background: 'rgba(0, 0, 0, 0.7)' }) - // 获得详情 - this.cardItem = item + try { + this.cardItem = item + this.showPrevious = item.sort > 0 + this.showNext = item.sort < this.allItem.length - 1 - // 查找下个详情 - const params = { paperId: this.paperId, quId: item.quId } - quDetail(params).then(response => { - console.log(response) + // 重置编辑器 + this.showEditor = false + this.code = '' + + const params = { paperId: this.paperId, quId: item.quId } + const response = await quDetail(params) this.quData = response.data - // 重置相关值 - this.radioValue = '' - this.multiValue = [] - - // 如果是编程题,重置编程相关数据 + // 如果是编程题,设置默认代码 if (this.quData.quType === 4) { - this.code = '// 请在此编写代码\n#include \n\nint main() {\n // 在此编写代码\n return 0;\n}' - this.runResult = null + await this.$nextTick() + this.code = this.defaultCode[this.language] + this.showEditor = true } - // 填充该题目的答案 - this.quData.answerList.forEach((item) => { - if ((this.quData.quType === 1 || this.quData.quType === 3) && item.checked) { - this.radioValue = item.id - } - - if (this.quData.quType === 2 && item.checked) { - this.multiValue.push(item.id) - } - }) - - // 关闭详情 loading.close() - }) + } catch (error) { + console.error('加载题目失败:', error) + this.$message.error('加载题目失败') + loading.close() + } }, // 试卷详情 @@ -461,6 +526,92 @@ export default { }) }, + initExtensions() { + // 创建行号和断点的装饰器 + const breakpointGutter = gutter({ + class: "breakpoint-gutter", + renderElement: () => { + const marker = document.createElement("div") + marker.className = "gutter-element" + return marker + } + }) + + const lineNumberGutter = gutter({ + class: "line-number-gutter", + lineMarker: (view, line) => { + const lineNo = view.state.doc.lineAt(line.from).number + const marker = document.createElement("div") + marker.textContent = lineNo + marker.className = "line-number" + if (this.breakpoints.has(lineNo)) { + marker.classList.add("has-breakpoint") + } + return new class extends GutterMarker { + toDOM() { return marker } + } + }, + domEventHandlers: { + click: (view, line) => { + const lineNo = view.state.doc.lineAt(line.from).number + if (this.breakpoints.has(lineNo)) { + this.breakpoints.delete(lineNo) + } else { + this.breakpoints.add(lineNo) + } + view.dispatch({}) + return true + } + } + }) + + this.extensions = [ + cpp(), + oneDark, + EditorView.lineWrapping, + lineNumberGutter, + breakpointGutter, + EditorView.theme({ + "&": { height: "100%" }, + ".cm-content": { padding: "0" }, + ".cm-line": { padding: "0 8px", height: "20px" }, + ".cm-gutters": { minHeight: "100%" } + }) + ] + }, + + onReady(editor) { + this.editor = editor + this.initExtensions() + }, + + clearBreakpoints() { + this.breakpoints.clear() + if (this.editor) { + this.editor.dispatch({}) + } + }, + + // 修改语言切换处理 + async handleLanguageChange(newVal) { + this.code = this.defaultCode[newVal] + await this.$nextTick() + this.initExtensions() + }, + + handleCodeChange(value) { + this.code = value + // 更新行数,并确保与编辑器内容同步 + this.$nextTick(() => { + this.lineCount = value.split('\n').length + // 同步滚动位置 + if (this.editor) { + const scrollInfo = this.editor.getScrollInfo() + this.$refs.gutter.scrollTop = scrollInfo.top + } + }) + }, + runProgrammingCode() { this.runLoading = true runCode({ @@ -475,40 +626,57 @@ export default { }) }, - submitProgrammingCode() { + // 提交编程题代码 + async submitProgrammingCode() { + if (!this.code.trim()) { + this.$message.warning('请输入代码') + return + } + this.submitLoading = true - submitCode({ - paperId: this.paperId, - quId: this.quData.quId, - language: this.language, - code: this.code - }).then(response => { - this.runResult = response.data - this.submitLoading = false + + try { + const response = await submitCode({ + paperId: this.paperId, + quId: this.quData.quId, + language: this.language, + code: this.code + }) if (response.data.success) { - // 标记为已回答 this.cardItem.answered = true - - this.$notify({ - title: '成功', - message: '代码提交成功!', - type: 'success', - duration: 2000 - }) + this.$message.success('代码提交成功!') } else { - this.$notify({ - title: '提示', - message: '代码提交失败,请检查您的代码', - type: 'warning', - duration: 2000 - }) + this.$message.warning(response.data.message || '代码提交失败,请检查您的代码') } - }).catch(() => { + } catch (error) { + console.error('提交代码失败:', error) + this.$message.error('提交失败,请稍后重试') + } finally { this.submitLoading = false - }) - } + } + }, + resetCode() { + if (this.currentQuestion && this.currentQuestion.type === 'programming') { + this.currentQuestion.userAnswer = this.currentQuestion.template || + '// 在这里编写你的代码\nfunction solution() {\n \n}'; + } + }, + + }, + + watch: { + language: { + handler: 'handleLanguageChange', + immediate: true + } + }, + + beforeUnmount() { + if (this.editor) { + this.editor.dispose() + } } } @@ -583,7 +751,7 @@ export default { } .problem-description { - margin-bottom: 20px; + margin-bottom: 10px; } .problem-description h3 { @@ -602,34 +770,825 @@ export default { .code-container { margin-top: 20px; + border: 1px solid #dcdfe6; + border-radius: 4px; + background: #fff; + display: flex; + flex-direction: column; + min-height: 500px; } - .language-selector { - margin-bottom: 10px; + .toolbar { + padding: 6px 8px; + border-bottom: 1px solid #dcdfe6; + display: flex; + align-items: center; + gap: 10px; + background: #f5f7fa; + } + + .editor-container { + flex: 1; + min-height: 400px; + background: #1e1e1e; + overflow: hidden; + } + + :deep(.cm-editor) { + height: 100%; + font-family: Consolas, Monaco, monospace; + font-size: 14px; + } + + :deep(.cm-gutters) { + display: flex; + background: #1e1e1e; + border-right: 1px solid #404040; + user-select: none; + } + + :deep(.breakpoint-gutter) { + width: 20px; + background: #1e1e1e; + } + + :deep(.line-number-gutter) { + min-width: 35px; + padding-right: 3px; + background: #1e1e1e; + } + + :deep(.gutter-element) { + position: relative; + height: 20px; + } + + :deep(.line-number) { + text-align: right; + padding: 0 8px; + color: #858585; + cursor: pointer; + height: 20px; + line-height: 20px; + font-size: 12px; + position: relative; + } + + :deep(.line-number.has-breakpoint) { + color: #d64040; + } + + :deep(.line-number.has-breakpoint::before) { + content: "⬤"; + position: absolute; + left: -14px; + font-size: 12px; + color: #d64040; + } + + :deep(.line-number:hover) { + background-color: rgba(255, 255, 255, 0.1); + } + + :deep(.cm-activeLineGutter) { + background-color: #2d3139 !important; + } + + :deep(.cm-activeLine) { + background-color: #2d3139; + } + + :deep(.cm-line) { + padding: 0 8px; + line-height: 20px; + font-family: Consolas, Monaco, monospace; + } + + /* 优化滚动条 */ + :deep(.cm-scroller::-webkit-scrollbar) { + width: 10px; + height: 10px; + } + + :deep(.cm-scroller::-webkit-scrollbar-track) { + background: #1e1e1e; + } + + :deep(.cm-scroller::-webkit-scrollbar-thumb) { + background: #404040; + border-radius: 5px; + } + + :deep(.cm-scroller::-webkit-scrollbar-corner) { + background: #1e1e1e; } .action-buttons { - margin-top: 15px; + padding: 6px 8px; + border-top: 1px solid #dcdfe6; + background: #f5f7fa; display: flex; justify-content: flex-end; } - .run-result { - margin-top: 20px; - padding-top: 20px; - border-top: 1px solid #ebeef5; + .qu-content { + display: flex; + flex-direction: column; + height: calc(100vh - 120px); } - .output-container, .expected-output-container { - margin-top: 15px; + .problem-description { + flex: 0 0 auto; } - .output-container pre, .expected-output-container pre { - background-color: #f5f7fa; - padding: 10px; - border-radius: 4px; + .code-container { + flex: 1 1 auto; + display: flex; + flex-direction: column; + min-height: 500px; + } + + .cm-editor { + height: 100%; + } + + .cm-scroller { overflow: auto; } + .code-textarea { + width: 100%; + height: 100%; + padding: 10px; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 14px; + line-height: 1.5; + border: none; + resize: none; + background: #1e1e1e; + color: #d4d4d4; + } + + .code-textarea:focus { + outline: none; + } + + :deep(.cm-editor) { + height: 100%; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 14px; + } + + :deep(.cm-scroller) { + overflow: auto; + padding: 8px; + } + + :deep(.cm-content) { + font-family: Consolas, Monaco, 'Andale Mono', monospace; + } + + :deep(.cm-line) { + padding: 0 4px; + line-height: 1.6; + } + + :deep(.cm-focused) { + outline: none; + } + + :deep(.cm-theme-dark) { + background-color: #1e1e1e; + } + + :deep(.cm-gutters) { + border-right: 1px solid #404040; + background-color: #1e1e1e; + } + + :deep(.cm-activeLineGutter) { + background-color: #2c313a; + } + + :deep(.cm-activeLine) { + background-color: #2c313a; + } + + .code-textarea { + display: none; + } + + .breakpoint-line::before { + content: "●"; + color: #ff4444; + position: absolute; + left: -20px; + font-size: 20px; + line-height: 1; + } + + :deep(.cm-gutterElement) { + cursor: pointer; + position: relative; + } + + :deep(.cm-gutterElement:hover) { + background-color: rgba(255, 255, 255, 0.1); + } + + .breakpoint-line { + background-color: rgba(255, 68, 68, 0.1); + } + + .programming-section { + margin: 20px 0; + padding: 20px; + background: #ffffff; + border-radius: 12px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); + } + + .question-content { + max-width: 1200px; + margin: 0 auto; + } + + .question-info { + display: flex; + gap: 12px; + margin: 16px 0; + } + + .info-tag { + display: inline-flex; + align-items: center; + padding: 8px 16px; + background: #f8fafc; + border-radius: 8px; + font-size: 14px; + color: #64748b; + transition: all 0.2s; + } + + .info-tag i { + margin-right: 8px; + color: #3b82f6; + } + + .description { + margin: 20px 0; + line-height: 1.6; + color: #1a202c; + } + + .test-cases-section { + margin: 24px 0; + } + + .section-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 16px; + } + + .section-header h4 { + margin: 0; + font-size: 18px; + color: #1a202c; + display: flex; + align-items: center; + gap: 8px; + } + + .section-header h4 i { + color: #3b82f6; + } + + .case-count { + font-size: 14px; + color: #64748b; + background: #f1f5f9; + padding: 4px 12px; + border-radius: 16px; + } + + .test-cases { + display: flex; + flex-direction: column; + gap: 16px; + } + + .test-case-card { + border: 1px solid #e2e8f0; + border-radius: 10px; + overflow: hidden; + transition: all 0.2s; + } + + .test-case-card:hover { + border-color: #cbd5e1; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); + } + + .test-case-header { + padding: 12px 16px; + background: #f8fafc; + display: flex; + justify-content: space-between; + align-items: center; + } + + .case-number { + font-weight: 500; + color: #64748b; + } + + .test-case-content { + padding: 16px; + } + + .test-input, .test-output { + margin: 8px 0; + } + + .label { + font-weight: 500; + color: #475569; + margin-bottom: 4px; + } + + code { + display: block; + background: #f8fafc; + padding: 8px 12px; + border-radius: 6px; + font-family: 'Fira Code', monospace; + font-size: 13px; + color: #334155; + border: 1px solid #e2e8f0; + } + + .editor-section { + margin: 24px 0; + } + + .editor-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0 0 8px 0; + } + + .editor-tabs { + display: flex; + gap: 2px; + } + + .tab { + padding: 8px 16px; + font-size: 14px; + color: #64748b; + border-radius: 6px; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + } + + .tab.active { + background: #f1f5f9; + color: #3b82f6; + font-weight: 500; + } + + .editor-actions { + display: flex; + gap: 12px; + } + + .action-btn { + padding: 8px 16px; + border: 1px solid #e2e8f0; + background: white; + border-radius: 6px; + color: #64748b; + font-size: 14px; + cursor: pointer; + display: flex; + align-items: center; + gap: 6px; + transition: all 0.2s; + } + + .action-btn:hover { + border-color: #cbd5e1; + background: #f8fafc; + } + + .submit-btn { + padding: 8px 20px; + background: #3b82f6; + color: white; + border: none; + border-radius: 6px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + transition: all 0.2s; + } + + .submit-btn:hover { + background: #2563eb; + transform: translateY(-1px); + } + + .code-editor-wrapper { + border-radius: 10px; + overflow: hidden; + border: 1px solid #e2e8f0; + height: 600px; + } + + .result-section { + margin: 24px 0; + padding: 20px; + background: #f8fafc; + border-radius: 10px; + } + + .result-content { + margin: 0; + padding: 16px; + background: white; + border-radius: 8px; + border: 1px solid #e2e8f0; + font-family: 'Fira Code', monospace; + font-size: 13px; + line-height: 1.5; + } + + @media (max-width: 1024px) { + .question-info { + flex-wrap: wrap; + } + + .programming-section { + padding: 16px; + } + } + + .programming-question { + background: #fff; + border-radius: 8px; + height: 100%; + display: flex; + flex-direction: column; + } + + .programming-header { + padding: 16px 20px; + border-bottom: 1px solid #e5e7eb; + } + + .title-section { + display: flex; + align-items: baseline; + margin-bottom: 12px; + } + + .question-number { + font-size: 18px; + font-weight: 500; + color: #374151; + margin-right: 8px; + } + + .title-section h3 { + font-size: 18px; + font-weight: 500; + color: #111827; + margin: 0; + } + + .meta-info { + display: flex; + gap: 12px; + } + + .info-tag { + display: inline-flex; + align-items: center; + padding: 4px 12px; + background: #f3f4f6; + border-radius: 4px; + font-size: 13px; + color: #4b5563; + } + + .info-tag i { + margin-right: 6px; + color: #6b7280; + } + + .problem-content { + display: flex; + flex: 1; + min-height: 0; + padding: 20px; + gap: 20px; + } + + .problem-description { + flex: 0 0 40%; + overflow-y: auto; + padding-right: 20px; + } + + .description-section { + margin-bottom: 24px; + } + + .description-section h4 { + font-size: 15px; + font-weight: 500; + color: #111827; + margin: 0 0 12px 0; + } + + .content { + font-size: 14px; + line-height: 1.6; + color: #374151; + } + + .test-cases { + margin-top: 24px; + } + + .test-cases h4 { + font-size: 15px; + font-weight: 500; + color: #111827; + margin: 0 0 12px 0; + } + + .test-case { + background: #f9fafb; + border: 1px solid #e5e7eb; + border-radius: 6px; + overflow: hidden; + } + + .case-item { + padding: 12px; + } + + .case-item:not(:last-child) { + border-bottom: 1px solid #e5e7eb; + } + + .case-header { + font-size: 13px; + font-weight: 500; + color: #6b7280; + margin-bottom: 8px; + } + + .case-content { + background: #ffffff; + border: 1px solid #e5e7eb; + border-radius: 4px; + padding: 12px; + margin: 0; + font-family: 'Fira Code', monospace; + font-size: 13px; + line-height: 1.5; + color: #374151; + } + + .code-section { + flex: 1; + display: flex; + flex-direction: column; + min-width: 0; + background: #f9fafb; + border: 1px solid #e5e7eb; + border-radius: 8px; + overflow: hidden; + } + + .editor-toolbar { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 12px; + background: #ffffff; + border-bottom: 1px solid #e5e7eb; + } + + .toolbar-left, .toolbar-right { + display: flex; + gap: 8px; + align-items: center; + } + + .language-select { + width: 120px; + } + + .language-select :deep(.el-input__wrapper) { + background-color: #f8fafc; + box-shadow: 0 0 0 1px #e5e7eb; + border-radius: 4px; + padding: 0 8px; + } + + .language-select :deep(.el-input__wrapper.is-focus) { + box-shadow: 0 0 0 1px #3b82f6; + } + + .language-select :deep(.el-input__inner) { + font-size: 13px; + color: #374151; + height: 32px; + } + + .language-select :deep(.el-select__caret) { + color: #6b7280; + font-size: 12px; + } + + /* 下拉菜单样式 */ + .el-select-dropdown.el-popper { + border: 1px solid #e5e7eb; + border-radius: 6px; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + } + + .el-select-dropdown__item { + font-size: 13px; + color: #374151; + height: 32px; + line-height: 32px; + } + + .el-select-dropdown__item.hover, + .el-select-dropdown__item:hover { + background-color: #f3f4f6; + } + + .el-select-dropdown__item.selected { + background-color: #eff6ff; + color: #3b82f6; + font-weight: 500; + } + + .toolbar-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + border: 1px solid #e5e7eb; + border-radius: 4px; + background: #ffffff; + color: #6b7280; + font-size: 13px; + cursor: pointer; + transition: all 0.2s; + } + + .toolbar-btn:hover { + background: #f3f4f6; + border-color: #d1d5db; + } + + .submit-btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 16px; + border: none; + border-radius: 4px; + background: #3b82f6; + color: #ffffff; + font-size: 13px; + font-weight: 500; + cursor: pointer; + transition: all 0.2s; + } + + .submit-btn:hover { + background: #2563eb; + } + + .code-editor { + flex: 1; + min-height: 0; + } + + .result-panel { + border-top: 1px solid #e5e7eb; + } + + .result-header { + padding: 12px; + background: #ffffff; + font-size: 13px; + font-weight: 500; + color: #374151; + display: flex; + align-items: center; + gap: 6px; + } + + .result-content { + margin: 0; + padding: 12px; + background: #ffffff; + font-family: 'Fira Code', monospace; + font-size: 13px; + line-height: 1.5; + color: #374151; + max-height: 200px; + overflow-y: auto; + } + + /* 自定义滚动条样式 */ + .problem-description::-webkit-scrollbar, + .result-content::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + .problem-description::-webkit-scrollbar-track, + .result-content::-webkit-scrollbar-track { + background: #f1f1f1; + } + + .problem-description::-webkit-scrollbar-thumb, + .result-content::-webkit-scrollbar-thumb { + background: #d1d5db; + border-radius: 4px; + } + + .problem-description::-webkit-scrollbar-thumb:hover, + .result-content::-webkit-scrollbar-thumb:hover { + background: #9ca3af; + } + + /* 修改布局相关的样式 */ + .problem-content.vertical { + flex-direction: column; + gap: 24px; + padding: 24px; + } + + .full-width { + width: 100%; + } + + .problem-description { + background: #fff; + border: 1px solid #e5e7eb; + border-radius: 8px; + padding: 24px; + } + + .test-case-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 16px; + margin-top: 16px; + } + + .code-section { + background: #f9fafb; + border: 1px solid #e5e7eb; + border-radius: 8px; + overflow: hidden; + height: 600px; /* 增加代码编辑器的高度 */ + } + + /* 其他样式保持不变 */ + .code-editor { + height: calc(100% - 48px); /* 减去工具栏的高度 */ + min-height: 400px; + } + + /* 调整工具栏样式 */ + .editor-toolbar { + height: 48px; + padding: 0 16px; + background: #fff; + border-bottom: 1px solid #e5e7eb; + display: flex; + align-items: center; + justify-content: space-between; + } +