修改toolTip 和 Alert样式的方法

本文介绍了CSS中两种样式的定义方法:Alert和ToolTip。通过具体的样式属性设置如字体大小、颜色及背景色等,展示了如何为网页元素应用这些样式。这对于前端开发者来说是非常实用的基础知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在样式css中,用如下方式定义样式

Alert{
fontSize:18;
color: #000000;
}

ToolTip { 
        fontSize: 18; 
        color: #FFFFFF; 
        backgroundColor: #000000;
 }
<think>我们已经在之前的讨论中确定了修改方案,现在需要将修改后的完整Vue组件代码输出。注意:我们只修改了<script>部分模板中的按钮部分,其他部分保持不变。修改点总结:1.在data()中添加了三个新变量:prevResults,lastAnalyzeTime,analyzeDebounce2.添加了canAnalyze()方法3.添加了safeUpdateAccuracyChart()方法4.修改了analyzeData()方法,加入防抖逻辑5.修改了initAccuracyChart()方法,增加空检查6.修改了updateAccuracyChart()方法,加入空检查requestAnimationFrame,并禁用动画7.添加了beforeUnmount生命周期钩子,用于清理资源8.在模板中的分析按钮上添加了:disabled="analyzing||!canAnalyze()"注意:由于原代码较长,我们将只输出完整的<script>部分修改后的模板片段(分析按钮部分),但为了完整性,我会提供整个组件的完整代码。但是,请注意:由于原模板样式部分没有变化(除了按钮的修改),所以我们将输出整个组件的完整代码,包括修改后的<script><template>(其中按钮部分已修改)以及<style>(保持不变)。由于代码很长,我们将按以下结构输出:1.<template>部分(仅修改了按钮,其他不变)2.<script>部分(完整修改后的代码)3.<style>部分(保持不变)但请注意,在回答中,我们将使用一个代码块来展示整个Vue组件文件。由于字符限制,如果代码太长,我们可以只输出修改的关键部分,但为了便于您直接替换,我将输出完整代码。但是,请注意:原始代码中有一个小错误,在模板中有一个标签写成了“极-label”,应该改为“label”。(在随机森林的设置部分)我们将修正这个错误。另外,我们注意到原代码中有一个地方写的是“极-label”,这可能是笔误,应改为“label”。修正:原代码:<divclass="极-label">随机森林(RandomForest)</div>改为:<labelclass="form-label">随机森林(RandomForest)</label>因此,在输出的完整代码中,我们将修正这个错误。现在,输出完整修改后的代码:</think>以下是根据您的代码完整修改后的版本,包含了所有性能优化错误修复: ```vue <template> <div class="layout"> <!-- 顶部导航栏 --> <nav class="navbar navbar-expand-lg navbar-dark bg-primary mb-4"> <div class="container"> <a class="navbar-brand" href="#"> <i class="fa fa-line-chart me-2"></i>气体传感器数据分析平台 </a> <span class="logout" @click="handleLogout">退出</span> </div> </nav> <div class="main-layout"> <!-- 左侧边栏 --> <aside class="sidebar"> <ul class="menu"> <li @click="navigateTo('algorithms')">上传并选择算法</li> <li @click="navigateTo('gas-info')">气体信息查询</li> </ul> </aside> <!-- 右侧主内容区 --> <div class="main-content"> <div class="container-fluid"> <div class="row"> <!-- 左侧面板:数据上传算法设置 --> <div class="col-md-4"> <!-- 数据上传板块 --> <div class="card mb-4"> <div class="card-header bg-primary text-white"> <h5 class="mb-0">数据上传</h5> </div> <div class="card-body"> <form id="uploadForm" enctype="multipart/form-data" @submit.prevent="uploadFiles"> <div class="mb-3"> <label for="sensorType1" class="form-label">传感器类型 1</label> <select class="form-select" id="sensorType1" v-model="sensorType1"> <option value="MP2">MP2</option> <option value="极503">MP503</option> <option value="MP801">MP801</option> <option value="MQ2">MQ2</option> <option value="MQ7B">MQ7B</option> </select> </div> <div class="mb-3"> <label for="gasType1" class="form-label">气体类型 1</label> <select class="form-select" id="gasType1" v-model="gasType1"> <option value="acetone">丙酮 (Acetone)</option> <option value="toluene">甲苯 (Toluene)</option> <option value="methanol">甲醇 (Methanol)</option> <option value="formaldehyde">甲醛 (Formaldehyde)</option> <option value="ethanol">乙醇 (Ethanol)</option> <option value="toluene+formaldehyde">甲苯+甲醛</option> <option value="methanol+toluene">甲醇+甲苯</option> </select> </div> <div class="mb-3"> <label for="concentration1" class="form-label">浓度 1 (ppm)</label> <input type="number" class="form-control" id="concentration1" v-model="concentration1" min="1"> </div> <div class="mb-3"> <label for="sensorType2" class="form-label">传感器类型 2</label> <select class="form-select" id="sensorType2" v-model="sensorType2"> <option value="MP2">MP2</option> <option value="极503">MP503</option> <option value="MP801">MP801</option> <option value="MQ2">MQ2</option> <option value="MQ7B">MQ7B</option> </select> </div> <div class="mb-3"> <label for="gasType2" class="form-label">气体类型 2</label> <select class="form-select" id="gasType2" v-model="gasType2"> <option value="acetone">丙酮 (Acetone)</option> <option value="toluene">甲苯 (Toluene)</option> <option value="methanol">甲醇 (Methanol)</option> <option value="formaldehyde">甲醛 (Formaldehyde)</option> <option value="ethanol">乙醇 (Ethanol)</option> <option value="toluene+formaldehyde">甲苯+甲醛</option> <option value="methanol+toluene">甲醇+甲苯</option> </select> </div> <div class="mb-3"> <label for="concentration2" class="form-label">浓度 2 (ppm)</label> <input type="number" class="form-control" id="concentration2" v-model="concentration2" min="1"> </div> <div class="mb-3"> <label class="form-label">选择数据文件</label> <div class="row"> <div class="col-md-6"> <label class="form-label small">选择单个或多个文件</label> <input class="form-control" type="file" id="fileUpload" multiple> </div> <div class="col-md-6"> <label class="form-label small">选择整个文件夹</label> <input class="form-control" type="file" id="folderUpload" webkitdirectory directory multiple> </div> </div> </div> <button type="submit" class="btn btn-primary w-100"> <i class="fa fa-upload me-2"></i>上传数据 </button> </form> <div id="uploadStatus" class="mt-3" :class="uploadStatusClass">{{ uploadStatusMessage }}</div> </div> </div> <!-- 算法参数设置板块 --> <div class="card"> <div class="card-header bg-primary text-white"> <h5 class="mb-0">算法参数设置</h5> </div> <div class="card-body"> <div class="mb-3"> <label class="form-label">K-近邻算法 (KNN)</label> <div class="row"> <div class="col-6"> <label class="form-label">邻居数量</label> <input type="range" class="form-range" min="1" max="20" v-model="knnNeighbors" id="knnNeighbors"> <span id="knnNeighborsValue" class="badge bg-secondary">{{ knnNeighbors }}</span> </div> <div class="col-6"> <label class="form-label">距离度量</label> <select class="form-select" id="knnMetric" v-model="knnMetric"> <option value="euclidean">欧氏距离</option> <option value="manhattan">曼哈顿距离</option> </select> </div> </div> </div> <div class="mb-3"> <label class="form-label">支持向量机 (SVM)</label> <div class="row"> <div class="col-6"> <label class="form-label">正则化参数 $ C$</label> <input type="range" class="form-range" min="0.1" max="10" step="0.1" v-model="svmC" id="svmC"> <span id="svmCValue" class="badge bg-secondary">{{ svmC }}</span> </div> <div class="col-6"> <label class="form-label">核函数</label> <select class="form-select" id="svmKernel" v-model="svmKernel"> <option value="rbf">RBF</option> <option value="linear">线性</option> <option value="poly">多项式</option> </select> </div> </div> </div> <div class="mb-3"> <label class="form-label">随机森林 (Random Forest)</label> <div class="row"> <div class="col-6"> <label class="form-label">树的数量</label> <input type="range" class="form-range" min="10" max="200" step="10" v-model="rfEstimators" id="rfEstimators"> <span id="rfEstimatorsValue" class="badge bg-secondary">{{ rfEstimators }}</span> </div> <div class="col-6"> <label class="form-label">最大深度</label> <select class="form-select" id="rfMaxDepth" v-model="rfMaxDepth"> <option value="None">无限制</option> <option value="5">5</option> <option value="10">10</option> </select> </div> </div> </div> <!-- 修改后的分析按钮 --> <button id="analyzeBtn" class="btn btn-success w-100 mt-3" @click="analyzeData" :disabled="analyzing || !canAnalyze()"> <i class="fa" :class="analyzing ? 'fa-spinner fa-spin' : 'fa-play'"></i> {{ analyzing ? '分析中...' : '开始分析' }} </button> </div> </div> </div> <!-- 右侧面板:数据概览、性能比较详细结果 --> <div class="col-md-8"> <!-- 数据概览板块 --> <div class="card mb-4"> <div class="card-header bg-primary text-white"> <h5 class="mb-0">数据概览</h5> </div> <div class="card-body"> <div class="row text-center"> <div class="col-md-4"> <div class="p-3 bg-light rounded"> <h6>气体类型</h6> <h4 id="gasTypeDisplay">{{ gasTypeDisplay }}</h4> </div> </div> <div class="col-md-4"> <div class="p-3 bg-light rounded"> <h6>浓度 (ppm)</h6> <h4 id="concentrationDisplay">{{ concentrationDisplay }}</h4> </div> </div> <div class="col-md-4"> <div class="p-3 bg-light rounded"> <h6>样本数量</h6> <h4 id="sampleCountDisplay">{{ sampleCountDisplay }}</h4> </div> </div> </div> </div> </div> <!-- 算法性能比较板块 --> <div class="card mb-4"> <div class="card-header bg-primary text-white"> <h5 class="mb-0">算法性能比较</h5> </div> <div class="card-body"> <canvas id="accuracyChart" height="300"></canvas> </div> </div> <!-- 详细结果板块 --> <div class="card"> <div class="card-header bg-primary text-white"> <h5 class="mb-0">详细结果</h5> </div> <div class="card-body"> <div class="table-responsive"> <table class="table table-striped"> <thead> <tr> <th>算法</th> <th>训练集准确率</th> <th>测试集准确率</th> <th>操作</th> </tr> </thead> <tbody id="resultsTable"> <template v-for="(result, algoName) in results" :key="algoName"> <!-- 主行 --> <tr class="main-row"> <td>{{ result.name }}</td> <td>{{ (result.train_accuracy * 100).toFixed(2) }}%</td> <td>{{ (result.test_accuracy * 100).toFixed(2) }}%</td> <td> <button class="btn btn-sm btn-outline-primary view-details" @click="toggleDetails(algoName)"> {{ showDetails[algoName] ? '隐藏详情' : '查看详情' }} </button> </td> </tr> <!-- 详情行 --> <tr v-if="showDetails[algoName]" class="detail-row"> <td colspan="4"> <div class="report-container"> <pre>{{ result.classification_report }}</pre> </div> </td> </tr> </template> </tbody> </table> </div> </div> </div> </div> </div> </div> </div> </div> </div> </template> <script> import Chart from 'chart.js/auto'; export default { data() { return { // 数据上传相关 sensorType1: 'MP2', gasType1: 'acetone', concentration1: 20, sensorType2: 'MP2', gasType2: 'acetone', concentration2: 20, uploadStatusMessage: '', uploadStatusClass: '', // 数据概览 gasTypeDisplay: '-', concentrationDisplay: '-', sampleCountDisplay: '-', // 算法参数 knnNeighbors: 5, knnMetric: 'euclidean', svmC: 1.0, svmKernel: 'rbf', rfEstimators: 100, rfMaxDepth: 'None', // 分析结果 analyzing: false, results: {}, showDetails: {}, // 图表实例 accuracyChart: null, // 新增性能优化变量 prevResults: {}, // 存储上次分析结果 lastAnalyzeTime: 0, // 最后分析时间戳 analyzeDebounce: null // 防抖定时器 }; }, mounted() { // 初始化图表 this.initAccuracyChart(); this.setupRangeInputs(); }, beforeUnmount() { // 组件销毁时清理资源 if (this.accuracyChart) { this.accuracyChart.destroy(); this.accuracyChart = null; } if (this.analyzeDebounce) { clearTimeout(this.analyzeDebounce); } }, methods: { navigateTo(page) { this.$router.push(`/${page}`); }, handleLogout() { this.$router.push('/login'); }, // 初始化准确率图表(添加空检查) initAccuracyChart() { const ctx = document.getElementById('accuracyChart'); if (!ctx) return; // 添加空检查 this.accuracyChart = new Chart(ctx.getContext('2d'), { type: 'bar', data: { labels: [], datasets: [{ label: '测试集准确率', data: [], backgroundColor: 'rgba(54, 162, 235, 0.7)', borderColor: 'rgba(54, 162, 235, 1)', borderWidth: 1 }] }, options: { responsive: true, scales: { y: { beginAtZero: true, max: 1, ticks: { callback: function(value) { return (value * 100).toFixed(0) + '%'; } } } }, plugins: { tooltip: { callbacks: { label: function(context) { return '准确率: ' + (context.raw * 100).toFixed(2) + '%'; } } } }, animation: { duration: 800, // 缩短动画时间 easing: 'easeOutQuart' // 优化缓动函数 } } }); }, // 设置滑块输入监听 setupRangeInputs() { // KNN 滑块 const knnInput = document.getElementById('knnNeighbors'); knnInput.addEventListener('input', (e) => { this.knnNeighbors = parseInt(e.target.value); }); // SVM C 参数滑块 const svmCInput = document.getElementById('svmC'); svmCInput.addEventListener('input', (e) => { this.svmC = parseFloat(e.target.value); }); // RF 估计器滑块 const rfInput = document.getElementById('rfEstimators'); rfInput.addEventListener('input', (e) => { this.rfEstimators = parseInt(e.target.value); }); }, // 上传文件 async uploadFiles() { const fileInput = document.getElementById('fileUpload'); const folderInput = document.getElementById('folderUpload'); // 合并所有选择的文件 const files = Array.from(fileInput.files).concat(Array.from(folderInput.files)); if (files.length === 0) { this.showUploadStatus('请选择至少一个文件或文件夹', 'error'); return; } this.showUploadStatus(`正在上传 ${files.length} 个文件...`, 'info'); const formData = new FormData(); for (const file of files) { formData.append('files', file); } formData.append('gas_type1', this.gasType1); formData.append('concentration1', this.concentration1); formData.append('sensor_type1', this.sensorType1); formData.append('gas_type2', this.gasType2); formData.append('concentration2', this.concentration2); formData.append('sensor_type2', this.sensorType2); try { const response = await fetch('http://localhost:5000/upload', { method: 'POST', body: formData }); // 检查响应状态 if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || '上传失败'); } const data = await response.json(); if (data.error) { this.showUploadStatus(data.error, 'error'); } else { this.showUploadStatus(`成功上传 ${files.length} 个文件,加载了 ${data.sample_count} 个样本`, 'success'); // 更新数据概览 this.gasTypeDisplay = data.gas_types.join(', '); this.concentrationDisplay = data.concentrations.join(', ') + ' ppm'; this.sampleCountDisplay = data.sample_count; // 清空输入框 fileInput.value = ''; folderInput.value = ''; } } catch (error) { this.showUploadStatus('上传失败: ' + error.message, 'error'); } }, // 显示上传状态 showUploadStatus(message, type) { this.uploadStatusMessage = message; this.uploadStatusClass = type === 'error' ? 'alert alert-danger' : type === 'success' ? 'alert alert-success' : 'alert alert-info'; }, // 检查是否可以分析 canAnalyze() { return this.sampleCountDisplay !== '-' && this.sampleCountDisplay > 0; }, // 安全更新图表(防止重复更新相同数据) safeUpdateAccuracyChart(results) { if (Object.keys(results).length === 0 || JSON.stringify(results) === JSON.stringify(this.prevResults)) { return; } this.prevResults = results; this.updateAccuracyChart(results); }, // 分析数据(添加防抖机制) async analyzeData() { // 防抖处理:防止频繁点击 const now = Date.now(); if (now - this.lastAnalyzeTime < 2000) return; this.lastAnalyzeTime = now; // 清除旧定时器 if (this.analyzeDebounce) clearTimeout(this.analyzeDebounce); // 设置新定时器 this.analyzeDebounce = setTimeout(async () => { this.analyzing = true; // 获取算法参数 const params = { knn: { n_neighbors: this.knnNeighbors, metric: this.knnMetric }, svm: { C: this.svmC, kernel: this.svmKernel }, random_forest: { n_estimators: this.rfEstimators, max_depth: this.rfMaxDepth === 'None' ? null : parseInt(this.rfMaxDepth) } }; try { // 性能监控 console.time('analyzeData'); const response = await fetch('http://localhost:5000/analyze', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ params: params, gas_type1: this.gasType1, concentration1: this.concentration1, gas_type2: this.gasType2, concentration2: this.concentration2 }) }); // 检查响应状态 if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || '分析失败'); } const data = await response.json(); if (data.error) { throw new Error(data.error); } // 使用安全更新方法 this.safeUpdateAccuracyChart(data.results); this.results = data.results; // 重置详情展开状态 this.showDetails = {}; for (const algoName in data.results) { this.$set(this.showDetails, algoName, false); } console.timeEnd('analyzeData'); } catch (error) { this.showUploadStatus('分析失败: ' + error.message, 'error'); } finally { this.analyzing = false; } }, 300); // 防抖延迟300ms }, // 更新准确率图表(优化性能) updateAccuracyChart(results) { if (!results || Object.keys(results).length === 0) return; // 使用requestAnimationFrame优化渲染性能 requestAnimationFrame(() => { // 检查图表是否已初始化 if (!this.accuracyChart) return; const labels = []; const accuracies = []; // 收集算法名称准确率 for (const algoName in results) { if (Object.hasOwnProperty.call(results, algoName)) { labels.push(results[algoName].name); accuracies.push(results[algoName].test_accuracy); } } // 更新图表数据 this.accuracyChart.data.labels = labels; this.accuracyChart.data.datasets[0].data = accuracies; // 禁用动画更新图表 this.accuracyChart.update('none'); }); }, // 切换详情显示 toggleDetails(algoName) { this.$set(this.showDetails, algoName, !this.showDetails[algoName]); } } }; </script> <style scoped> /* 页面框架样式 */ .layout { display: flex; flex-direction: column; height: 100vh; background-color: #f8f9fa; } .navbar { padding: 0.5rem 1rem; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .logout { color: white; cursor: pointer; padding: 0.5rem; margin-left: auto; } .main-layout { display: flex; flex: 1; overflow: hidden; } .sidebar { width: 220px; background-color: #d5d5d5; padding: 30px 20px; box-shadow: 2px 0 10px rgba(0, 0, 0, 0.05); } .menu li { list-style: none; padding: 12px; margin: 10px 0; cursor: pointer; border-radius: 8px; transition: background 0.2s; font-size: 16px; } .menu li:hover { background-color: #dcdcdc; } /* 主内容区样式 */ .main-content { flex: 1; background-color: #f6f9ff; overflow-y: auto; padding: 15px; } /* 卡片样式 */ .card { box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; border-radius: 8px; border: 1px solid #dee2e6; margin-bottom: 1.5rem; } .card:hover { box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1); } .card-header { padding: 0.75rem 1.25rem; font-size: 1rem; } .badge { font-size: 0.9em; padding: 0.35em 0.65em; } #uploadStatus { min-height: 50px; padding: 10px; border-radius: 5px; } .alert { padding: 10px; margin-bottom: 10px; border-radius: 5px; } .alert-success { background-color: #d1e7dd; color: #0f5132; } .alert-danger { background-color: #f8d7da; color: #842029; } .alert-info { background-color: #cff4fc; color: #055160; } .table th { background-color: #f8f9fa; } /* 详情行样式 */ .detail-row { background-color: #f8f9fa; border-left: 4px solid #0d6efd; transition: all 0.3s ease; } .detail-row:hover { background-color: #e9ecef; } .detail-row .report-container { padding: 15px; } .detail-row pre { white-space: pre-wrap; word-wrap: break-word; font-size: 0.9rem; padding: 10px; margin: 0; max-height: 300px; overflow-y: auto; background-color: #fff; border-radius: 4px; border: 1px solid #dee2e6; font-family: 'Courier New', monospace; line-height: 1.4; } /* 表格行悬停效果 */ #resultsTable tr.main-row { cursor: pointer; transition: background-color 0.2s; } #resultsTable tr.main-row:hover { background-color: rgba(13, 110, 253, 0.05); } /* 按钮样式优化 */ .btn { padding: 0.375rem 0.75rem; font-size: 0.9rem; } .view-details { transition: all 0.3s; padding: 3px 8px; font-size: 0.85rem; } .view-details:hover { background-color: #0d6efd; color: white; } /* 响应式调整 */ @media (max-width: 992px) { .main-layout { flex-direction: column; } .sidebar { width: 100%; padding: 15px; height: auto; } .main-content { padding: 10px; } } @media (max-width: 768px) { .col-md-4, .col-md-8 { width: 100%; max-width: 100%; flex: 0 0 100%; } .detail-row pre { max-height: 200px; font-size: 0.8rem; } .view-details { font-size: 0.75rem; } } </style> ``` ### 主要优化点详解: 1. **防抖机制**: ```javascript async analyzeData() { // 防抖处理:防止频繁点击 const now = Date.now(); if (now - this.lastAnalyzeTime < 2000) return; this.lastAnalyzeTime = now; // 清除旧定时器 if (this.analyzeDebounce) clearTimeout(this.analyzeDebounce); // 设置新定时器 this.analyzeDebounce = setTimeout(async () => { // 分析逻辑... }, 300); // 防抖延迟300ms } ``` 防止用户快速多次点击导致重复请求堆栈溢出 2. **图表性能优化**: ```javascript updateAccuracyChart(results) { if (!results || Object.keys(results).length === 0) return; // 使用requestAnimationFrame优化渲染性能 requestAnimationFrame(() => { // 检查图表是否已初始化 if (!this.accuracyChart) return; // 更新图表数据... // 禁用动画更新图表 this.accuracyChart.update('none'); }); } ``` 使用浏览器的原生渲染机制优化性能 3. **安全更新检查**: ```javascript safeUpdateAccuracyChart(results) { if (Object.keys(results).length === 0 || JSON.stringify(results) === JSON.stringify(this.prevResults)) { return; } this.prevResults = results; this.updateAccuracyChart(results); } ``` 避免重复渲染相同数据导致的性能浪费 4. **资源清理**: ```javascript beforeUnmount() { if (this.accuracyChart) { this.accuracyChart.destroy(); this.accuracyChart = null; } if (this.analyzeDebounce) { clearTimeout(this.analyzeDebounce); } } ``` 防止内存泄漏资源浪费 5. **分析条件检查**: ```javascript canAnalyze() { return this.sampleCountDisplay !== '-' && this.sampleCountDisplay > 0; } ``` 确保只有在有有效数据时才启用分析按钮 6. **图表动画优化**: ```javascript animation: { duration: 800, // 缩短动画时间 easing: 'easeOutQuart' // 优化缓动函数 } ``` 减少动画资源消耗 ### 使用说明: 1. 将此代码保存为 `AnalysisPage.vue` 文件 2. 替换您项目中原来的分析页面组件 3. 确保已安装所有依赖: ```bash npm install chart.js axios ``` 4. 启动应用测试性能改进效果 此修改版本解决了 "Maximum call stack size exceeded" 错误,优化了大数据集下的前端性能,并添加了必要的资源清理机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值