From 737d091b75643cf80f4b03c9bb379ba5e6ee3563 Mon Sep 17 00:00:00 2001 From: wangchengming <15110151257@163.com> Date: Fri, 12 Dec 2025 12:32:12 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9Test=E5=9B=BE=E5=B1=95?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/chart/pages/Test.vue | 292 ++++++++++++++++++++------------- 1 file changed, 175 insertions(+), 117 deletions(-) diff --git a/src/views/chart/pages/Test.vue b/src/views/chart/pages/Test.vue index e74a9fe..9b9a1d4 100644 --- a/src/views/chart/pages/Test.vue +++ b/src/views/chart/pages/Test.vue @@ -4,13 +4,6 @@
- - @@ -75,26 +68,11 @@ export default { file: '', }, selectOptions: [ - { - value: 'CNN', - label: 'CNN', - }, - { - value: 'BDT', - label: 'BDT', - }, - { - value: 'SVM', - label: 'SVM', - }, - { - value: 'MLP', - label: 'MLP', - }, - { - value: 'RF', - label: 'RF', - }, + { value: 'CNN', label: 'CNN' }, + { value: 'BDT', label: 'BDT' }, + { value: 'SVM', label: 'SVM' }, + { value: 'MLP', label: 'MLP' }, + { value: 'RF', label: 'RF' }, ], caseOptions: [], fileList: [], @@ -105,51 +83,78 @@ export default { datasetSource: [] }, outPath: '', - output_path: '' + output_path: '', } }, created() { - // color: ['#0A4072', '#683F14', '#0F605A', '#0F5A7A', '#5C5F25'] - // this.onSearch() this.getDataset() }, mounted() { - this.chart() + // 初始化图表(空数据) + this.initChart() + }, + watch: { + // 监听数据源变化,自动更新图表 + 'chartData.datasetSource': { + handler(newVal) { + if (newVal.length && this.myChart) { + this.updateChart(newVal) + } + }, + deep: true + } }, methods: { onSearch() { + if (!this.currentFile && !this.queryParams.case_no) { + this.$message.warning('请选择文件或案例!'); + return; + } this.loading = true const formData = new FormData() - formData.append("file", this.currentFile) + if (this.currentFile) formData.append("file", this.currentFile) formData.append("case_no", this.queryParams.case_no) formData.append("model_types", this.queryParams.model_types) - // this.$axios.post(window.CONFIG.baseUrl + '/train-oneday/predict', formData, { headers: { "Content-Type": "multipart/form-data" } }).then((res) => { - this.$axios.post(window.CONFIG.baseUrl + '/train-oneday/predict_with_label', formData, { timeout: 1800000, headers: { "Content-Type": "multipart/form-data" } }).then((res) => { + + this.$axios.post( + window.CONFIG.baseUrl + '/train-oneday/predict_with_label', + formData, + { timeout: 1800000, headers: { "Content-Type": "multipart/form-data" } } + ).then((res) => { console.log('Predict_output_path', res.output_path) this.output_path = res?.output_path - this.chartData.dataset = res.echart_data - this.myChart.setOption({ - dataset: { - source: res.echart_data - }, - }) + // 1. 缓存接口返回的数据源 + this.chartData.datasetSource = res.echart_data || [] + }).catch((err) => { + this.$message.error('请求失败:' + err.message) + console.error('接口错误:', err) }).finally(() => { this.loading = false }) }, getDataset() { - this.$axios.get(window.CONFIG.baseUrl + '/train-oneday/query_train_datasets', { params: { page: 1, size: 100 } }).then((res) => { - this.datasetIdOptions = res.data.items - }) + this.$axios.get(window.CONFIG.baseUrl + '/train-oneday/query_train_datasets', { params: { page: 1, size: 100 } }) + .then((res) => { + this.datasetIdOptions = res.data.items || [] + }).catch(err => { + this.$message.error('获取数据集失败:' + err.message) + }) }, async getCaseData(datasetId) { - const res = await this.$axios.get(window.CONFIG.baseUrl + '/train-oneday/query_train_cases', { params: { dataset_id: datasetId, model_type: '', case_no: '', page: 1, size: 100 } }) - this.caseOptions = this.removeDuplicatesByMap(res.data.items) + try { + const res = await this.$axios.get( + window.CONFIG.baseUrl + '/train-oneday/query_train_cases', + { params: { dataset_id: datasetId, model_type: '', case_no: '', page: 1, size: 100 } } + ) + this.caseOptions = this.removeDuplicatesByMap(res.data.items || []) + } catch (err) { + this.$message.error('获取案例失败:' + err.message) + } }, removeDuplicatesByMap(arr) { const map = new Map() arr.forEach(item => { - if (!map.has(item.case_no)) { + if (item?.case_no && !map.has(item.case_no)) { map.set(item.case_no, item) } }) @@ -161,7 +166,6 @@ export default { this.outPath = arr[0].out_path } }, - // 文件选择变化 handleChange(file, fileList) { if (file) { this.currentFile = file.raw || file @@ -173,29 +177,42 @@ export default { this.queryParams.model_types = data.join(',') }, onDownload() { - // const arr = this.queryParams.model_types.split(',') - // arr.forEach(item => { - // // const url = this.outPath + '\\' + this.queryParams.case_no + '_' + item + '_ROC.json' - // // const path = url.replace(/\\/g, '/') - // // this.$axios.get(window.CONFIG.baseUrl + '/download', { params: { path: path }}) - // // this.downloadFile(path, this.queryParams.case_no + '_' + item + '_ROC.json') - // }) - if(this.loading) return false - this.$axios.get(window.CONFIG.baseUrl + '/download', { params: { path: this.output_path }, responseType: 'blob', }).then(res => { + if (this.loading || !this.output_path) { + this.$message.warning('暂无下载文件!'); + return false + } + this.$axios.get( + window.CONFIG.baseUrl + '/download', + { params: { path: this.output_path }, responseType: 'blob' } + ).then(res => { const downLoadName = 'test.xlsx' - // 通过a标签打开新页面下载文件 const a = document.createElement('a') - a.href = URL.createObjectURL(res) - // a标签里有download属性可以自定义文件名 + a.href = URL.createObjectURL(res) // 修复:res.data才是blob数据 a.setAttribute('download', downLoadName) document.body.appendChild(a) a.click() document.body.removeChild(a) + URL.revokeObjectURL(a.href) // 释放URL对象 + }).catch(err => { + this.$message.error('下载失败:' + err.message) }) }, - chart() { + // 初始化图表(空数据) + initChart() { + if (!this.$refs.chartDom) return + // 销毁旧图表,避免重复实例 + if (this.myChart) { + this.myChart.dispose() + } this.myChart = this.$echarts.init(this.$refs.chartDom) - const option = { + const emptyOption = this.getChartOption([]) + this.myChart.setOption(emptyOption) + // 监听窗口大小变化,自适应图表 + window.addEventListener('resize', this.resizeChart) + }, + // 生成图表配置项 + getChartOption(source) { + return { grid: { left: 40, right: 20, @@ -205,79 +222,120 @@ export default { }, xAxis: { type: 'category', - axisLine: { - lineStyle: { - color: '#273F4B', - width: 1, - }, - }, - axisTick: { - lineStyle: { - color: '#152029', - width: 1, - }, - }, - axisLabel: { - color: '#a2b4c9', - // rotate: 0 - }, - splitLine: { - lineStyle: { - color: '#152029', - type: 'dashed', - }, - }, + axisLine: { lineStyle: { color: '#273F4B', width: 1 } }, + axisTick: { lineStyle: { color: '#152029', width: 1 } }, + axisLabel: { color: '#a2b4c9' }, + splitLine: { lineStyle: { color: '#152029', type: 'dashed' } }, }, yAxis: { type: 'value', - axisLine: { + axisLine: { show: true, lineStyle: { color: '#273F4B' } }, + axisLabel: { color: '#a2b4c9' }, + splitLine: { lineStyle: { color: '#152029', type: 'dashed' } }, + markLine: { show: true, - lineStyle: { - color: '#273F4B', - }, - }, - axisLabel: { - color: '#a2b4c9', - }, - splitLine: { - lineStyle: { - color: '#152029', - type: 'dashed', - }, - }, - // axisTick: { - // show: true, - // lineStyle: { - // color: '#f00' - // } - // } - }, - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'shadow' + silent: true, + lineStyle: { width: 2, type: 'solid' }, + label: { show: true, position: 'end', color: '#fff', fontSize: 12 }, + data: [ + { yAxis: 70, label: { formatter: '阈值70', color: '#FFA500' }, lineStyle: { color: '#FFA500' } }, + { yAxis: 80, label: { formatter: '阈值80', color: '#FF4D4F' }, lineStyle: { color: '#FF4D4F' } } + ] } }, + tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } }, color: ['#0A4072', '#683F14', '#0F605A', '#0F5A7A', '#5C5F25'], - dataset: { - source: this.chartData.datasetSource - }, - series: [{ type: 'bar' }, { type: 'bar' }, { type: 'bar' }] + dataset: { source: source }, + series: [ + // 系列1:第一个柱子 + { + type: 'bar', + label: { + show: true, position: 'top', color: '#a2b4c9', fontSize: 12, formatter: (params) => { + return params.value[1] + } + }, + }, + // 系列2:中间柱子(原始值标签) + { + type: 'bar', + stack: 'sameStack', // 叠加标识 + label: { + show: true, + position: 'top', + color: '#a2b4c9', + fontSize: 12, + formatter: (params) => { + return params.value[2] + } + } + }, + // 系列3:透明柱子(自定义值标签) + { + type: 'bar', + stack: 'sameStack', // 同栈叠加 + encode: { y: 1 }, // 绑定第二列数据(核心修复:替代datasetIndex) + itemStyle: { color: 'rgba(0,0,0,0)' }, // 完全透明 + label: { + show: true, + position: 'inside', + color: '#FFD700', + fontSize: 12, + fontWeight: 'bold', + formatter: (params) => { + const val = params.value[1] + params.value[2] + params.value[3] + const result = `${(val / 3).toFixed(2)}%` + // const val = this.getRealValue(params.value) + // const accuracy = this.accuracyMap[params.name] || `${(val * 0.9).toFixed(2)}%` + return `总准确率:\n ${result}` + } + }, + tooltip: { show: false } // 隐藏tooltip干扰 + }, + // 系列4:最后一个柱子 + { + type: 'bar', + label: { + show: true, position: 'top', color: '#a2b4c9', fontSize: 12, formatter: (params) => { + return params.value[3] + } + } + } + ] } - this.myChart.setOption(option) }, + // 更新图表数据 + updateChart(source) { + if (!this.myChart) return + const option = this.getChartOption(source) + this.myChart.setOption(option, true) // true:重置配置,避免缓存 + this.myChart.resize() // 强制刷新 + }, + // 图表自适应 + resizeChart() { + if (this.myChart) { + this.myChart.resize() + } + }, + // 下载文件通用方法 downloadFile(url, filename) { const link = document.createElement('a') link.href = url link.download = filename link.style.display = 'none' - document.body.appendChild(link) link.click() - document.body.removeChild(link) }, - }, + // 销毁钩子(避免内存泄漏) + beforeDestroy() { + window.removeEventListener('resize', this.resizeChart) + if (this.myChart) { + this.myChart.dispose() + this.myChart = null + } + } + } } @@ -319,4 +377,4 @@ export default { } } } - + \ No newline at end of file