|
|
@@ -55,7 +55,7 @@
|
|
|
<!-- 右侧工作区域 -->
|
|
|
<div class="main-work" :style="{ background: showExamDetail ? 'transparent' : '#ebf3ff' }">
|
|
|
<!-- 头部 -->
|
|
|
- <div class="work-header">
|
|
|
+ <div class="work-header" v-if="showExamDetail">
|
|
|
<h2>考试工坊</h2>
|
|
|
</div>
|
|
|
|
|
|
@@ -67,237 +67,121 @@
|
|
|
<p>正在加载历史记录...</p>
|
|
|
</div>
|
|
|
<!-- 考试工坊主界面 -->
|
|
|
- <div v-if="!showExamDetail" class="exam-workshop-card">
|
|
|
- <!-- 左侧配置区域 -->
|
|
|
- <div class="config-section">
|
|
|
- <!-- 1. 选择试卷类型 -->
|
|
|
- <div class="config-item">
|
|
|
- <div class="config-header">
|
|
|
- <div class="step-number">1</div>
|
|
|
- <h3>选择试卷类型</h3>
|
|
|
- </div>
|
|
|
- <div class="type-cards">
|
|
|
- <div
|
|
|
- v-for="(type, key) in projectTypes"
|
|
|
- :key="key"
|
|
|
- :class="['type-card', { active: selectedProjectType === key }]"
|
|
|
- @click="(isGenerating || selectedFile) ? null : selectProjectType(key)"
|
|
|
- :style="{ cursor: (isGenerating || selectedFile) ? 'not-allowed' : 'pointer', opacity: (isGenerating || selectedFile) ? '0.5' : '1' }"
|
|
|
- >
|
|
|
- <img :src="type.icon" :alt="type.name" class="type-icon" />
|
|
|
- <span>{{ type.name }}</span>
|
|
|
+ <div v-if="!showExamDetail" class="exam-workshop-card app-container">
|
|
|
+ <!-- 中间主操作区 -->
|
|
|
+ <main class="main-content">
|
|
|
+
|
|
|
+ <div class="form-group">
|
|
|
+ <label class="form-label">试卷名称</label>
|
|
|
+ <input type="text" class="form-control" v-model="examName" maxlength="32" placeholder="请输入试卷名称..." :disabled="isGenerating">
|
|
|
+ <div class="char-count">{{ examName?.length || 0 }}/32</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 2. 选择生成方式 -->
|
|
|
- <div class="config-item">
|
|
|
- <div class="config-header">
|
|
|
- <div class="step-number">2</div>
|
|
|
- <h3>选择生成方式</h3>
|
|
|
- </div>
|
|
|
- <div class="generation-methods">
|
|
|
- <div
|
|
|
- :class="['method-card', { active: selectedFunction === 'ai' }]"
|
|
|
- @click="(isGenerating || selectedFile) ? null : selectFunction('ai')"
|
|
|
- :style="{ cursor: (isGenerating || selectedFile) ? 'not-allowed' : 'pointer', opacity: (isGenerating || selectedFile) ? '0.5' : '1' }"
|
|
|
- >
|
|
|
- <img
|
|
|
- :src="aiIcon"
|
|
|
- alt="智能生成试卷"
|
|
|
- class="method-icon"
|
|
|
- />
|
|
|
- <div class="method-content">
|
|
|
- <h4>智能生成试卷</h4>
|
|
|
- <p>基于AI技术,根据所选类型自动生成完整试卷</p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div
|
|
|
- :class="['method-card', { active: selectedFunction === 'ppt' }]"
|
|
|
- @click="isGenerating ? null : (selectedFunction === 'ppt' && !selectedFile ? triggerFileUpload() : selectFunction('ppt'))"
|
|
|
- :style="{ cursor: isGenerating ? 'not-allowed' : 'pointer', opacity: isGenerating ? '0.5' : '1' }"
|
|
|
- >
|
|
|
- <img
|
|
|
- :src="pptIcon"
|
|
|
- alt="从PPT生成考题"
|
|
|
- class="method-icon"
|
|
|
- />
|
|
|
- <div class="method-content">
|
|
|
- <h4>从PPT生成考题</h4>
|
|
|
- <p>上传培训PPT,智能提取关键内容生成考题(单个文件可上传20M内)</p>
|
|
|
+ <div class="form-group">
|
|
|
+ <label class="form-label">出题依据内容</label>
|
|
|
+ <textarea class="form-control" v-model="questionBasis" placeholder="在此输入知识点、章节或培训内容..." :disabled="isGenerating || selectedFile"></textarea>
|
|
|
|
|
|
- <!-- PPT文件预览区域 -->
|
|
|
- <div v-if="selectedFunction === 'ppt' && selectedFile" class="ppt-file-preview">
|
|
|
- <div class="file-preview">
|
|
|
- <div class="file-icon">{{ selectedFile.icon }}</div>
|
|
|
- <div class="file-info">
|
|
|
- <div class="file-name">{{ selectedFile.name }}</div>
|
|
|
- <div class="file-size">{{ formatFileSize(selectedFile.size) }}</div>
|
|
|
+ <div class="ppt-upload-section" @click="!isGenerating && !selectedFile ? triggerFileUpload() : null">
|
|
|
+ <div class="ppt-upload-content">
|
|
|
+ <div class="ppt-upload-icon-wrapper">
|
|
|
+ <span class="material-symbols-outlined" style="font-size: 28px; color: #4b5563;">cloud_upload</span>
|
|
|
+ </div>
|
|
|
+ <div class="ppt-upload-text-wrapper">
|
|
|
+ <div class="ppt-upload-title">从PPT生成考题</div>
|
|
|
+ <div class="ppt-upload-hint">上传培训PPT,智能提取关键内容生成考题(单个文件可上传20M内)</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <span class="material-symbols-outlined ppt-arrow">chevron_right</span>
|
|
|
+
|
|
|
+ <div v-if="selectedFile" class="file-status-badge" @click.stop>
|
|
|
+ <span class="file-name truncate">已上传: {{ selectedFile.name }}</span>
|
|
|
+ <span @click.stop="removeSelectedFile" class="remove-btn">×</span>
|
|
|
</div>
|
|
|
- <button class="remove-file-btn" @click="removeSelectedFile">
|
|
|
- <span class="remove-icon">×</span>
|
|
|
- </button>
|
|
|
- </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 3. 试卷配置 -->
|
|
|
- <div class="config-item">
|
|
|
- <div class="config-header">
|
|
|
- <div class="step-number">3</div>
|
|
|
- <h3>试卷配置</h3>
|
|
|
- </div>
|
|
|
- <div class="exam-config-container">
|
|
|
- <!-- 左侧配置区域 -->
|
|
|
- <div class="config-left">
|
|
|
- <div class="config-row">
|
|
|
- <div class="config-group">
|
|
|
- <label>试卷名称</label>
|
|
|
- <div class="input-wrapper">
|
|
|
- <input
|
|
|
- v-model="examName"
|
|
|
- type="text"
|
|
|
- placeholder="请输入试卷名称"
|
|
|
- class="config-input"
|
|
|
- maxlength="32"
|
|
|
- @input="validateExamName"
|
|
|
- :disabled="isGenerating || selectedFile"
|
|
|
- />
|
|
|
- <span class="char-count-inline" :class="{ 'warning': examName.length >= 18 }">
|
|
|
- {{ examName.length }}/32
|
|
|
- </span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="config-group">
|
|
|
- <label>试卷总分</label>
|
|
|
- <div class="score-input">
|
|
|
- <input
|
|
|
- v-model="totalScore"
|
|
|
- type="number"
|
|
|
- class="config-input"
|
|
|
- min="1"
|
|
|
- max="1000"
|
|
|
- @input="validateTotalScore"
|
|
|
- :disabled="isGenerating || selectedFile"
|
|
|
- />
|
|
|
- <span class="unit">分</span>
|
|
|
- </div>
|
|
|
+ <!-- =============== 题型配置区域 开始 =============== -->
|
|
|
+ <div class="config-section">
|
|
|
+ <div class="config-header">
|
|
|
+ <h3>题型配置</h3>
|
|
|
+ <div class="total-score">试卷总分 {{ calculatedTotalScore }}</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
|
|
|
- <!-- 题型选择与分数分配 -->
|
|
|
- <div class="section-title">题型选择与分数分配</div>
|
|
|
- <div class="question-types">
|
|
|
- <div
|
|
|
- class="question-type"
|
|
|
- v-for="(type, index) in questionTypes"
|
|
|
- :key="index"
|
|
|
- >
|
|
|
- <div class="type-row">
|
|
|
- <span class="type-name">{{ type.name }}</span>
|
|
|
- <div class="progress-bar">
|
|
|
- <div
|
|
|
- class="progress-fill"
|
|
|
- :style="{
|
|
|
- width:
|
|
|
- ((type.scorePerQuestion * type.questionCount) /
|
|
|
- totalScore) *
|
|
|
- 100 +
|
|
|
- '%',
|
|
|
- }"
|
|
|
- ></div>
|
|
|
+ <!-- 动态渲染各题型 -->
|
|
|
+ <div class="question-types-grid">
|
|
|
+ <div class="question-type-card" v-for="(type, index) in questionTypes" :key="index">
|
|
|
+ <div class="question-type-header">
|
|
|
+ <div class="question-type-title">{{ type.name }}</div>
|
|
|
+ <div class="question-type-score">
|
|
|
+ 每题 <input type="number" class="score-input" v-model.number="type.scorePerQuestion" min="1" max="100" :disabled="isGenerating"> 分
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="slider-container">
|
|
|
+ <span class="slider-label">数量</span>
|
|
|
+ <input type="range" class="question-slider" v-model.number="type.questionCount" min="0" :max="type.max || 50" :disabled="isGenerating">
|
|
|
+ <span class="question-count" style="text-align: right; min-width: 40px;">{{ type.questionCount }} 题</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- <div class="score-config">
|
|
|
- <span>每题</span>
|
|
|
- <input
|
|
|
- v-model="type.scorePerQuestion"
|
|
|
- type="number"
|
|
|
- class="score-input-field"
|
|
|
- min="1"
|
|
|
- max="99"
|
|
|
- @input="validateScorePerQuestion(type)"
|
|
|
- :disabled="isGenerating || selectedFile"
|
|
|
- />
|
|
|
- <span>分</span>
|
|
|
- <span>一共</span>
|
|
|
- <input
|
|
|
- v-model="type.questionCount"
|
|
|
- type="number"
|
|
|
- class="count-input-field"
|
|
|
- min="1"
|
|
|
- max="99"
|
|
|
- @input="validateQuestionCount(type)"
|
|
|
- :disabled="isGenerating || selectedFile"
|
|
|
- />
|
|
|
- <span>题</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+
|
|
|
+ <div class="action-buttons">
|
|
|
+ <button class="clear-btn" @click="clearSettings" :disabled="isGenerating">
|
|
|
+ <span class="material-symbols-outlined" style="font-size: 18px;">delete</span>
|
|
|
+ 清空当前配置
|
|
|
+ </button>
|
|
|
+ <button class="generate-btn" @click="generateExam" :disabled="isGenerating">
|
|
|
+ <span class="material-symbols-outlined" v-if="!isGenerating">auto_awesome</span>
|
|
|
+ <span class="material-symbols-outlined animate-spin" v-else>autorenew</span>
|
|
|
+ {{ isGenerating ? '生成中...' : '开始智能生成试卷' }}
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
+ <!-- =============== 题型配置区域 结束 =============== -->
|
|
|
+ </main>
|
|
|
|
|
|
- <!-- 右侧预览面板 -->
|
|
|
- <div class="preview-panel">
|
|
|
- <div class="preview-header">
|
|
|
- <img :src="previewIcon" alt="预览" class="preview-icon" />
|
|
|
- <h3>预览</h3>
|
|
|
- </div>
|
|
|
- <div class="preview-content">
|
|
|
- <h4 class="preview-title">{{ examName || "试卷名称" }}</h4>
|
|
|
- <div class="question-breakdown">
|
|
|
- <div
|
|
|
- class="breakdown-item"
|
|
|
- v-for="(type, index) in questionTypes"
|
|
|
- :key="index"
|
|
|
- >
|
|
|
- <div class="breakdown-row">
|
|
|
- <span class="breakdown-left"
|
|
|
- >{{ type.romanNumeral }}、{{ type.name }} (每题{{
|
|
|
- type.scorePerQuestion
|
|
|
- }}分,共{{
|
|
|
- type.scorePerQuestion * type.questionCount
|
|
|
- }}分)</span
|
|
|
- >
|
|
|
- <span class="breakdown-right">{{ type.questionCount }}题</span>
|
|
|
+ <!-- =============== 实时预览区域 开始 =============== -->
|
|
|
+ <aside class="preview-panel">
|
|
|
+ <div class="preview-header">
|
|
|
+ <h3>实时预览</h3>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="preview-name-card">
|
|
|
+ <div class="preview-name-label">试卷名称</div>
|
|
|
+ <div class="preview-title" :style="{ fontStyle: examName ? 'normal' : 'italic' }">{{ examName || '未命名试卷...' }}</div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="preview-section">
|
|
|
+ <div class="preview-section-title">结构大纲</div>
|
|
|
+
|
|
|
+ <div class="preview-item" v-for="(type, index) in questionTypes" :key="index">
|
|
|
+ <div class="preview-item-top">
|
|
|
+ <div class="preview-item-left">
|
|
|
+ <div class="preview-dot" :style="{ backgroundColor: ['#2563eb', '#60a5fa', '#93c5fd', '#dbeafe'][index % 4] }"></div>
|
|
|
+ <span class="preview-type-name">{{ type.name }}</span>
|
|
|
+ </div>
|
|
|
+ <span class="preview-type-count">{{ type.questionCount }}题</span>
|
|
|
+ </div>
|
|
|
+ <div class="preview-item-bottom">
|
|
|
+ <span class="preview-type-score">{{ type.questionCount * type.scorePerQuestion }} 分</span>
|
|
|
</div>
|
|
|
- </div>
|
|
|
</div>
|
|
|
- <div class="divider"></div>
|
|
|
- <div class="calculated-score-row">
|
|
|
- <span class="calculated-label">配置总分</span>
|
|
|
- <span class="calculated-value">{{ calculatedTotalScore }}分</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="preview-footer">
|
|
|
+ <div class="preview-total">
|
|
|
+ <span>配置总分</span>
|
|
|
+ <span class="preview-total-score" style="color: #000000; font-size: 24px;">{{ totalScore }}</span>
|
|
|
</div>
|
|
|
- <div class="total-score-row">
|
|
|
- <span class="total-label">试卷总分</span>
|
|
|
- <span class="total-value">{{ totalScore }}分</span>
|
|
|
+ <div class="preview-total" style="margin-top: 20px; font-size: 20px; color: #000000;">
|
|
|
+ <span>试卷总分</span>
|
|
|
+ <span style="color: var(--primary-color); font-size: 24px;">{{ calculatedTotalScore }}</span>
|
|
|
</div>
|
|
|
- </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 底部操作按钮 -->
|
|
|
- <div class="bottom-actions">
|
|
|
- <button class="clear-btn" @click="clearSettings" :disabled="isGenerating || selectedFile">
|
|
|
- <img :src="clearIcon" alt="一键清除" class="clear-icon" />
|
|
|
- </button>
|
|
|
- <button class="generate-btn" @click="generateExam" :disabled="isGenerating">
|
|
|
- <img v-if="!isGenerating" :src="generateIcon" alt="生成试卷" class="generate-icon" />
|
|
|
- <span v-else class="generating-text">
|
|
|
- 生成中<span class="loading-dots">
|
|
|
- <span class="dot"></span>
|
|
|
- <span class="dot"></span>
|
|
|
- <span class="dot"></span>
|
|
|
- </span>
|
|
|
- </span>
|
|
|
- </button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ </aside>
|
|
|
+ <!-- =============== 实时预览区域 结束 =============== -->
|
|
|
</div>
|
|
|
|
|
|
+
|
|
|
<!-- 考试详情页 -->
|
|
|
<div v-if="showExamDetail" class="exam-detail-card">
|
|
|
<!-- 详情页头部 -->
|
|
|
@@ -900,10 +784,10 @@ const projectTypes = {
|
|
|
|
|
|
// 题型配置
|
|
|
const questionTypes = ref([
|
|
|
- { name: "单选题", scorePerQuestion: 5, questionCount: 5, romanNumeral: "一" },
|
|
|
- { name: "判断题", scorePerQuestion: 3, questionCount: 5, romanNumeral: "二" },
|
|
|
- { name: "多选题", scorePerQuestion: 8, questionCount: 5, romanNumeral: "三" },
|
|
|
- { name: "简答题", scorePerQuestion: 10, questionCount: 2, romanNumeral: "四" },
|
|
|
+ { name: "单选题", scorePerQuestion: 0, questionCount: 0, romanNumeral: "一" },
|
|
|
+ { name: "判断题", scorePerQuestion: 0, questionCount: 0, romanNumeral: "二" },
|
|
|
+ { name: "多选题", scorePerQuestion: 0, questionCount: 0, romanNumeral: "三" },
|
|
|
+ { name: "简答题", scorePerQuestion: 0, questionCount: 0, romanNumeral: "四" },
|
|
|
]);
|
|
|
|
|
|
// 保存初始配置
|
|
|
@@ -1002,10 +886,10 @@ const createNewChat = async () => {
|
|
|
} else {
|
|
|
// 如果没有初始配置,使用默认配置
|
|
|
questionTypes.value = [
|
|
|
- { name: "单选题", scorePerQuestion: 2, questionCount: 8, romanNumeral: "一" },
|
|
|
- { name: "判断题", scorePerQuestion: 2, questionCount: 5, romanNumeral: "二" },
|
|
|
- { name: "多选题", scorePerQuestion: 3, questionCount: 5, romanNumeral: "三" },
|
|
|
- { name: "简答题", scorePerQuestion: 10, questionCount: 2, romanNumeral: "四" },
|
|
|
+ { name: "单选题", scorePerQuestion: 0, questionCount: 0, romanNumeral: "一" },
|
|
|
+ { name: "判断题", scorePerQuestion: 0, questionCount: 0, romanNumeral: "二" },
|
|
|
+ { name: "多选题", scorePerQuestion: 0, questionCount: 0, romanNumeral: "三" },
|
|
|
+ { name: "简答题", scorePerQuestion: 0, questionCount: 0, romanNumeral: "四" },
|
|
|
];
|
|
|
}
|
|
|
|
|
|
@@ -1118,13 +1002,12 @@ const clearSettings = () => {
|
|
|
// 根据当前选择的工程类型设置试卷名称
|
|
|
const projectTypeName = projectTypes[selectedProjectType.value].name;
|
|
|
examName.value = `${projectTypeName}工程施工技术考核`;
|
|
|
- totalScore.value = 100;
|
|
|
- questionTypes.value = [
|
|
|
- { name: "单选题", scorePerQuestion: 2, questionCount: 8, romanNumeral: "一" },
|
|
|
- { name: "判断题", scorePerQuestion: 2, questionCount: 5, romanNumeral: "二" },
|
|
|
- { name: "多选题", scorePerQuestion: 3, questionCount: 5, romanNumeral: "三" },
|
|
|
- { name: "简答题", scorePerQuestion: 10, questionCount: 2, romanNumeral: "四" },
|
|
|
- ];
|
|
|
+ totalScore.value = 0; // 清空时配置总分也应该为 0
|
|
|
+ // 保留原数组引用,更新每个对象的属性,避免破坏 Vue 3 响应式绑定
|
|
|
+ questionTypes.value.forEach(type => {
|
|
|
+ type.scorePerQuestion = 0;
|
|
|
+ type.questionCount = 0;
|
|
|
+ });
|
|
|
console.log("清除设置");
|
|
|
};
|
|
|
|
|
|
@@ -3663,11 +3546,12 @@ onUnmounted(() => {
|
|
|
/* 工作内容区域 */
|
|
|
.work-content {
|
|
|
flex: 1;
|
|
|
- padding: 22px;
|
|
|
+ padding: 0;
|
|
|
// overflow-y: auto;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- align-items: center;
|
|
|
+ align-items: stretch;
|
|
|
+ height: 100%;
|
|
|
|
|
|
/* 隐藏滚动条样式 */
|
|
|
&::-webkit-scrollbar {
|
|
|
@@ -3684,14 +3568,519 @@ onUnmounted(() => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+.app-container {
|
|
|
+ --primary-color: #0d6efd;
|
|
|
+ --danger-color: #dc3545;
|
|
|
+ --warning-color: #ffc107;
|
|
|
+ --border-color: #dee2e6;
|
|
|
+ --bg-light: #f8f9fa;
|
|
|
+ --text-dark: #212529;
|
|
|
+ --text-muted: #6c757d;
|
|
|
+
|
|
|
+ display: flex;
|
|
|
+ height: 100%;
|
|
|
+ width: 100%;
|
|
|
+ padding: 0 !important;
|
|
|
+ background-color: #f5f5f5;
|
|
|
+ overflow: hidden;
|
|
|
+ margin: 0 !important;
|
|
|
+ max-width: 100% !important;
|
|
|
+ border-radius: 0 !important;
|
|
|
+ box-shadow: none !important;
|
|
|
+
|
|
|
+ /* 中间主操作区 */
|
|
|
+ .main-content {
|
|
|
+ flex: 1;
|
|
|
+ padding: 20px;
|
|
|
+ overflow-y: hidden;
|
|
|
+ background: white;
|
|
|
+ scrollbar-width: none; /* Firefox */
|
|
|
+ -ms-overflow-style: none; /* IE and Edge */
|
|
|
+ }
|
|
|
+ .main-content::-webkit-scrollbar {
|
|
|
+ display: none; /* Chrome, Safari and Opera */
|
|
|
+ }
|
|
|
+
|
|
|
+ .form-group {
|
|
|
+ margin-bottom: 12px;
|
|
|
+ max-width: 1150px; /* 限制输入框模块的最大宽度 */
|
|
|
+ margin-left: auto;
|
|
|
+ margin-right: auto; /* 使其在工作区居中 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .form-label {
|
|
|
+ font-weight: 600;
|
|
|
+ margin-bottom: 6px;
|
|
|
+ display: block;
|
|
|
+ font-size: 14px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .form-control {
|
|
|
+ width: 100%;
|
|
|
+ border: 1px solid rgba(0, 0, 0, 0.06); /* 统一边框 */
|
|
|
+ border-radius: 12px; /* 统一圆角 */
|
|
|
+ padding: 12px 16px; /* 稍微增加内边距让它看起来更像卡片 */
|
|
|
+ font-size: 14px;
|
|
|
+ transition: all 0.3s;
|
|
|
+ box-sizing: border-box;
|
|
|
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); /* 统一阴影 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .form-control:focus {
|
|
|
+ outline: none;
|
|
|
+ border-color: var(--primary-color);
|
|
|
+ box-shadow: 0 0 0 3px rgba(13, 110, 253, 0.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ textarea.form-control {
|
|
|
+ resize: none;
|
|
|
+ height: 250px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .char-count {
|
|
|
+ text-align: right;
|
|
|
+ font-size: 12px;
|
|
|
+ color: var(--text-muted);
|
|
|
+ margin-top: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* PPT上传区域 */
|
|
|
+ .ppt-upload-section {
|
|
|
+ background: white;
|
|
|
+ border: 1px solid rgba(0, 0, 0, 0.06); /* 统一边框 */
|
|
|
+ border-radius: 12px; /* 统一圆角 */
|
|
|
+ padding: 16px 20px;
|
|
|
+ margin-top: 40px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); /* 统一阴影 */
|
|
|
+ position: relative;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ppt-upload-section:hover {
|
|
|
+ border-color: var(--primary-color);
|
|
|
+ box-shadow: 0 8px 24px rgba(13, 110, 253, 0.12); /* 悬浮时加深发光阴影 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .ppt-upload-content {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ppt-upload-icon-wrapper {
|
|
|
+ width: 48px;
|
|
|
+ height: 48px;
|
|
|
+ background: #f3f4f6;
|
|
|
+ border-radius: 12px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ppt-upload-text-wrapper {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ppt-upload-title {
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1f2937;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ppt-upload-hint {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #6b7280;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ppt-arrow {
|
|
|
+ color: #9ca3af;
|
|
|
+ font-size: 24px;
|
|
|
+ transition: transform 0.3s;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ppt-upload-section:hover .ppt-arrow {
|
|
|
+ color: var(--primary-color);
|
|
|
+ transform: translateX(2px);
|
|
|
+ }
|
|
|
+
|
|
|
+ .file-status-badge {
|
|
|
+ position: absolute;
|
|
|
+ bottom: -40px;
|
|
|
+ left: 0;
|
|
|
+ background: #ebf3ff;
|
|
|
+ color: var(--primary-color);
|
|
|
+ padding: 8px 16px;
|
|
|
+ border-radius: 8px;
|
|
|
+ font-size: 13px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ border: 1px solid rgba(13, 110, 253, 0.1);
|
|
|
+ max-width: 300px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .file-name {
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+
|
|
|
+ .remove-btn {
|
|
|
+ color: #ef4444;
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ cursor: pointer;
|
|
|
+ padding: 0 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .remove-btn:hover {
|
|
|
+ color: #b91c1c;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* =============== 题型配置区域 样式开始 =============== */
|
|
|
+ .config-section {
|
|
|
+ margin-top: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .config-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center; /* 改为靠左对齐,而不是两端对齐 */
|
|
|
+ align-items: center;
|
|
|
+ gap: 960px; /* 控制“题型配置”和“试卷总分”之间的固定间距 */
|
|
|
+ margin-bottom: 6px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .config-header h3 {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+
|
|
|
+ .total-score {
|
|
|
+ background: var(--bg-light);
|
|
|
+ padding: 8px 16px;
|
|
|
+ border-radius: 20px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: var(--primary-color);
|
|
|
+ }
|
|
|
+
|
|
|
+ .question-types-grid {
|
|
|
+ display: grid;
|
|
|
+ /* 为了减小卡片宽度,我们不再让它们自动拉伸占满,而是指定最大宽度并居中,或者留出更大的列间距 */
|
|
|
+ grid-template-columns: repeat(2, minmax(0, 500px));
|
|
|
+ justify-content: center; /* 让网格居中,而不是两端拉伸 */
|
|
|
+ gap: 20px 150px; /* 行间距(高度)20px,列间距(宽度)40px */
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .question-type-card {
|
|
|
+ background: white; /* 改为白色背景 */
|
|
|
+ border-radius: 12px;
|
|
|
+ padding: 16px 20px; /* 根据截图稍微调大内边距以容纳阴影内容 */
|
|
|
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); /* 加深阴影 */
|
|
|
+ border: 1px solid rgba(0, 0, 0, 0.06); /* 稍微加深边框线 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .question-type-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 20px; /* 稍微增加与滑动条的间距 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .question-type-title {
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 16px; /* 字体稍微调大 */
|
|
|
+ color: #1f2937;
|
|
|
+ }
|
|
|
+
|
|
|
+ .question-type-score {
|
|
|
+ font-size: 13px;
|
|
|
+ color: #3b82f6; /* 改变字体颜色为蓝色系 */
|
|
|
+ background: #eff6ff; /* 添加淡蓝色背景 */
|
|
|
+ padding: 4px 12px;
|
|
|
+ border-radius: 20px; /* 胶囊形状 */
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .score-input {
|
|
|
+ width: 32px;
|
|
|
+ text-align: center;
|
|
|
+ border: none; /* 移除输入框边框 */
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 0;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #2563eb; /* 加深数字颜色 */
|
|
|
+ background: transparent; /* 背景透明,融入胶囊 */
|
|
|
+ transition: all 0.3s;
|
|
|
+ -webkit-appearance: textfield;
|
|
|
+ -moz-appearance: textfield;
|
|
|
+ appearance: textfield;
|
|
|
+ }
|
|
|
+
|
|
|
+ .score-input::-webkit-outer-spin-button,
|
|
|
+ .score-input::-webkit-inner-spin-button {
|
|
|
+ -webkit-appearance: none;
|
|
|
+ margin: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .score-input:focus {
|
|
|
+ outline: none;
|
|
|
+ background: white; /* 聚焦时背景变白 */
|
|
|
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
|
|
|
+ }
|
|
|
+
|
|
|
+ .slider-container {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .slider-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #4b5563;
|
|
|
+ font-weight: 500;
|
|
|
+ min-width: 40px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .question-slider {
|
|
|
+ flex: 1;
|
|
|
+ height: 6px;
|
|
|
+ -webkit-appearance: none;
|
|
|
+ appearance: none;
|
|
|
+ background: #e5e7eb; /* 滑动条底色调浅 */
|
|
|
+ border-radius: 3px;
|
|
|
+ outline: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .question-slider::-webkit-slider-thumb {
|
|
|
+ -webkit-appearance: none;
|
|
|
+ appearance: none;
|
|
|
+ width: 18px; /* 滑块调大一点 */
|
|
|
+ height: 18px;
|
|
|
+ background: #2563eb; /* 蓝色滑块 */
|
|
|
+ border: 2px solid white; /* 添加白色边框 */
|
|
|
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1); /* 滑块阴影 */
|
|
|
+ border-radius: 50%;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s;
|
|
|
+ }
|
|
|
+
|
|
|
+ .question-slider::-webkit-slider-thumb:hover {
|
|
|
+ background: #1d4ed8;
|
|
|
+ transform: scale(1.1);
|
|
|
+ }
|
|
|
+
|
|
|
+ .question-count {
|
|
|
+ font-weight: bold; /* 数字加粗 */
|
|
|
+ font-size: 15px;
|
|
|
+ color: #1f2937;
|
|
|
+ min-width: 40px;
|
|
|
+ text-align: right;
|
|
|
+ }
|
|
|
+
|
|
|
+ .action-buttons {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-top: 16px;
|
|
|
+ padding-top: 16px;
|
|
|
+ border-top: 1px solid var(--border-color);
|
|
|
+ }
|
|
|
+
|
|
|
+ .clear-btn {
|
|
|
+ background: white;
|
|
|
+ border: 1px solid rgba(0, 0, 0, 0.06); /* 统一边框 */
|
|
|
+ border-radius: 8px; /* 添加圆角 */
|
|
|
+ color: var(--text-muted);
|
|
|
+ font-size: 14px;
|
|
|
+ cursor: pointer;
|
|
|
+ padding: 8px 16px;
|
|
|
+ transition: all 0.3s;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 6px;
|
|
|
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); /* 统一阴影 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .clear-btn:hover {
|
|
|
+ color: var(--danger-color);
|
|
|
+ border-color: rgba(220, 53, 69, 0.2); /* 悬浮时边框微红 */
|
|
|
+ box-shadow: 0 6px 20px rgba(220, 53, 69, 0.1); /* 悬浮时带红色的发光阴影 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .generate-btn {
|
|
|
+ background: var(--primary-color);
|
|
|
+ color: white;
|
|
|
+ border: none;
|
|
|
+ padding: 10px 24px;
|
|
|
+ border-radius: 8px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .generate-btn:hover:not(:disabled) {
|
|
|
+ background: #0b5ed7;
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 4px 12px rgba(13, 110, 253, 0.3);
|
|
|
+ }
|
|
|
+
|
|
|
+ .generate-btn:disabled {
|
|
|
+ background: #93c5fd; /* 浅蓝色背景 */
|
|
|
+ cursor: not-allowed;
|
|
|
+ box-shadow: 0 4px 12px rgba(147, 197, 253, 0.4); /* 浅蓝色阴影 */
|
|
|
+ opacity: 0.9;
|
|
|
+ }
|
|
|
+ /* =============== 题型配置区域 样式结束 =============== */
|
|
|
+
|
|
|
+ /* =============== 实时预览区域 样式开始 =============== */
|
|
|
+ .preview-panel {
|
|
|
+ width: 320px;
|
|
|
+ background: #f7f9fb; /* 匹配截图背景 */
|
|
|
+ border-left: 1px solid var(--border-color);
|
|
|
+ padding: 24px;
|
|
|
+ overflow-y: hidden;
|
|
|
+ flex-shrink: 0;
|
|
|
+ scrollbar-width: none;
|
|
|
+ -ms-overflow-style: none;
|
|
|
+ }
|
|
|
+ .preview-panel::-webkit-scrollbar {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-header {
|
|
|
+ margin-bottom: 24px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-header h3 {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #1f2937;
|
|
|
+ margin: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-name-card {
|
|
|
+ background: white;
|
|
|
+ border-radius: 16px;
|
|
|
+ padding: 16px 20px;
|
|
|
+ margin-bottom: 24px;
|
|
|
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); /* 加深阴影 */
|
|
|
+ border: 1px solid rgba(0, 0, 0, 0.06); /* 稍微加深边框线 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-name-label {
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #4b5563;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-title {
|
|
|
+ font-size: 15px;
|
|
|
+ color: #9ca3af;
|
|
|
+ line-height: 1.4;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-section {
|
|
|
+ margin-bottom: 24px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-section-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #4b5563;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-item {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 4px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-item-top {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-item-left {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-dot {
|
|
|
+ width: 6px;
|
|
|
+ height: 6px;
|
|
|
+ border-radius: 50%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-type-name {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #1f2937;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-type-count {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1f2937;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-item-bottom {
|
|
|
+ padding-left: 14px; /* Align with text (6px dot + 8px gap) */
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-type-score {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #9ca3af;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-footer {
|
|
|
+ margin-top: 24px;
|
|
|
+ padding-top: 20px;
|
|
|
+ border-top: 1px solid #e5e7eb;
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-total {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #000000;
|
|
|
+ padding: 0 10px; /* 左右各加16px的内边距,使文字向中间靠拢 */
|
|
|
+ }
|
|
|
+
|
|
|
+ .preview-total-score {
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+ /* =============== 实时预览区域 样式结束 =============== */
|
|
|
+}
|
|
|
+
|
|
|
.exam-workshop-card {
|
|
|
background: white;
|
|
|
width: 100%;
|
|
|
- height: 960px;
|
|
|
- padding: 32px 32px 14px 32px;
|
|
|
- border-radius: 16px;
|
|
|
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
|
|
- max-width: 1528px;
|
|
|
+ height: 100%;
|
|
|
+ padding: 0;
|
|
|
+ border-radius: 0;
|
|
|
+ box-shadow: none;
|
|
|
+ max-width: 100%;
|
|
|
|
|
|
.config-section {
|
|
|
flex: 1;
|