diff --git a/src/store/modules/permission.js b/src/store/modules/permission.js index 53ae199..87ede25 100644 --- a/src/store/modules/permission.js +++ b/src/store/modules/permission.js @@ -222,6 +222,18 @@ const usePermissionStore = defineStore( "link": null }, }, + { + "name": "Test", + "path": "Test", + "hidden": false, + "component": "chart/pages/Test", + "meta": { + "title": "Test", + "icon": "log", + "noCache": false, + "link": null + }, + }, { "name": "Predict", "path": "Predict", diff --git a/src/views/chart/layout/SideMenu.vue b/src/views/chart/layout/SideMenu.vue index d4e3e5a..6b60654 100644 --- a/src/views/chart/layout/SideMenu.vue +++ b/src/views/chart/layout/SideMenu.vue @@ -107,6 +107,11 @@ export default { name: 'Config chart', url: '/system/chart/ConfigureChart', }, + { + id: '27', + name: 'Test', + url: '/system/chart/Test', + }, { id: '23', name: 'Predict', diff --git a/src/views/chart/pages/MonitorResult.vue b/src/views/chart/pages/MonitorResult.vue index 4247fce..79c918a 100644 --- a/src/views/chart/pages/MonitorResult.vue +++ b/src/views/chart/pages/MonitorResult.vue @@ -4,55 +4,41 @@
- - - - + + - - + + - - + + - Select + Select
-
-
+
+ +
+
+
@@ -73,60 +59,76 @@ 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: [], modelTypes: [], currentFile: null, myChart: null, + myChart2: null, + myChart3: null, + legendArr: [], chartData: { - seriesData: [] + seriesData: [], + seriesData2: [], + seriesData3: [] }, outPath: '' } }, - created () { - // color: ['#0A4072', '#683F14', '#0F605A', '#0F5A7A', '#5C5F25'] - // this.onSearch() + created() { this.getDataset() }, mounted() { this.chart() + this.chart2() + this.chart3() + // 监听窗口大小变化,自适应图表 + window.addEventListener('resize', () => { + this.myChart?.resize() + this.myChart2?.resize() + this.myChart3?.resize() + }) + }, + beforeDestroy() { + // 销毁图表,避免内存泄漏 + this.myChart?.dispose() + this.myChart2?.dispose() + this.myChart3?.dispose() + window.removeEventListener('resize', () => { + this.myChart?.resize() + this.myChart2?.resize() + this.myChart3?.resize() + }) }, methods: { 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 => console.error('获取数据集失败:', err)) }, 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) { + console.error('获取案例失败:', err) + this.caseOptions = [] + } }, 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) } }) @@ -134,120 +136,166 @@ export default { }, caseChange(id) { const arr = this.caseOptions.filter(item => item.case_no === id) - if(arr.length) { - this.outPath = arr[0].out_path - } + this.outPath = arr.length ? arr[0].out_path : '' }, modelTypeChange(data) { this.queryParams.model_types = data.join(',') }, async onDownload() { + // 前置校验 + if (!this.datasetId || !this.queryParams.case_no || !this.queryParams.model_types) { + this.$message.warning('请选择数据集、案例和模型类型') + return + } + const arr = this.queryParams.model_types.split(',') try { - const requests = arr.map(item => { + this.loading = true + this.legendArr = [] + // 1. 构建请求列表(先不执行Promise) + const requestList = arr.map(item => { + this.legendArr.push(item) const url = this.outPath + '\\' + this.queryParams.case_no + '_' + item + '_ROC.json' const path = url.replace(/\\/g, '/') - return this.$axios.get(window.CONFIG.baseUrl + '/download', { params: { path: path } }) - }) - const responses = await Promise.all(requests) - responses.forEach(pItem => { - if (this.chartData.seriesData.length) { - this.chartData.seriesData.forEach(series => { - pItem.forEach(item => { - if (series.name === item.name) { - series.data = [...series.data, ...item.data] - } - }) - }) - } else { - const arr = [...pItem] - arr.forEach(item => { - item.type = 'line' - }) - - this.chartData.seriesData = arr + // 返回:图例名称 + 异步请求(未执行) + return { + legendName: item, + request: this.$axios.get(window.CONFIG.baseUrl + '/download', { params: { path: path } }) } }) - debugger - this.myChart.setOption({ - series: this.chartData.seriesData - }) + + // 2. 等待所有请求完成,获取响应数据 + const responses = await Promise.all( + requestList.map(item => + item.request.then(res => { + // 打印完整响应,找到数据的真实路径 + return { + legendName: item.legendName, + // 先临时兜底,后续根据打印结果调整 + data: res + } + }).catch(err => { + console.error(`获取${item.legendName}数据失败:`, err) + return { legendName: item.legendName, data: [] } + }) + ) + ) + + // 3. 重置图表数据(避免多次选择累加错误) + this.chartData = { + seriesData: [], + seriesData2: [], + seriesData3: [] + } + + // 4. 处理响应数据,构建series + responses.forEach((item) => { + const { legendName, data } = item + const data0 = data[0].data + const data1 = data[1].data + const data2 = data[2].data + + // 构建每个图表的series项 + this.chartData.seriesData.push({ + name: legendName, + type: 'line', + data: data0, + smooth: true, // 可选:折线平滑 + symbol: 'none' // 可选:隐藏数据点 + }) + this.chartData.seriesData2.push({ + name: legendName, + type: 'line', + data: data1, + smooth: true, + symbol: 'none' + }) + this.chartData.seriesData3.push({ + name: legendName, + type: 'line', + data: data2, + smooth: true, + symbol: 'none' + }) + }) + // 5. 更新ECharts(同时更新图例) + this.updateChart(this.myChart, this.chartData.seriesData) + this.updateChart(this.myChart2, this.chartData.seriesData2) + this.updateChart(this.myChart3, this.chartData.seriesData3) } catch (error) { - console.error(error.message) + console.error('数据加载失败:', error.message) + this.$message.error('数据加载失败,请重试') + } finally { + this.loading = false } }, - chart() { - this.myChart = this.$echarts.init(this.$refs.chartDom) - const option = { + // 封装图表更新方法(复用) + updateChart(chartInstance, seriesData) { + if (!chartInstance) return + chartInstance.setOption({ + legend: { + data: this.legendArr // 关联图例数据 + }, + series: seriesData + }) + }, + // 初始化图表(抽离公共配置) + initChartOption() { + return { + legend: { + top: 0, + right: 20, + orient: 'horizontal', + textStyle: { color: '#a2b4c9', fontSize: 12 }, + itemGap: 15, + itemWidth: 12, + itemHeight: 8, + emphasis: { textStyle: { color: '#fff' } } + }, grid: { left: 40, right: 20, - bottom: 10, - top: 20, + bottom: 30, // 增加底部间距,避免标签被截断 + top: 35, // 给图例留空间 containLabel: true, }, xAxis: { type: 'value', - 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: { - show: true, - lineStyle: { - color: '#273F4B', - }, - }, - axisLabel: { - color: '#a2b4c9', - }, - splitLine: { - lineStyle: { - color: '#152029', - type: 'dashed', - }, - }, - // axisTick: { - // show: true, - // lineStyle: { - // color: '#f00' - // } - // } + axisLine: { show: true, lineStyle: { color: '#273F4B' } }, + axisLabel: { color: '#a2b4c9' }, + splitLine: { lineStyle: { color: '#152029', type: 'dashed' } }, }, tooltip: { trigger: 'axis', - axisPointer: { - type: 'shadow' - } + axisPointer: { type: 'shadow' }, + textStyle: { color: '#fff' }, + backgroundColor: 'rgba(0,0,0,0.7)' }, color: ['#0A4072', '#683F14', '#0F605A', '#0F5A7A', '#5C5F25'], series: [] } - this.myChart.setOption(option) }, - }, + chart() { + this.myChart = this.$echarts.init(this.$refs.chartDom) + this.myChart.setOption(this.initChartOption()) + }, + chart2() { + this.myChart2 = this.$echarts.init(this.$refs.chartDom2) + this.myChart2.setOption(this.initChartOption()) + }, + chart3() { + this.myChart3 = this.$echarts.init(this.$refs.chartDom3) + this.myChart3.setOption(this.initChartOption()) + }, + } } @@ -258,6 +306,7 @@ export default { color: #fff; display: flex; flex-direction: column; + .section-head { height: 32px; padding: 0 13px; @@ -266,22 +315,29 @@ export default { font-family: MICROGRAMMADMEDEXT; font-size: 20px; font-weight: bold; + line-height: 32px; // 垂直居中 } + .list-box { flex: 1; display: flex; flex-direction: column; + padding: 0 10px; // 增加内边距 + .filter-wrap { height: 50px; margin-top: 16px; - ::v-deep .el-form-item__label { - font-size: 16px; - color: #a7bacf; - } } + .chart-wrap { flex: 1; + margin-top: 10px; + + // 确保图表容器有高度 + &>div { + min-height: 300px; + } } } } - + \ No newline at end of file diff --git a/src/views/chart/pages/Predict.vue b/src/views/chart/pages/Predict.vue index a326a92..1585c48 100644 --- a/src/views/chart/pages/Predict.vue +++ b/src/views/chart/pages/Predict.vue @@ -59,7 +59,7 @@ style="height: 32px; margin-left: 12px;"> Upload - Select + Download @@ -134,7 +134,7 @@ export default { 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, { headers: { "Content-Type": "multipart/form-data" } }).then((res) => { + this.$axios.post(window.CONFIG.baseUrl + '/train-oneday/predict', formData, { headers: { "Content-Type": "multipart/form-data" } }).then((res) => { this.chartData.dataset = res.echart_data this.myChart.setOption({ dataset: { diff --git a/src/views/chart/pages/Test.vue b/src/views/chart/pages/Test.vue new file mode 100644 index 0000000..547fcc6 --- /dev/null +++ b/src/views/chart/pages/Test.vue @@ -0,0 +1,313 @@ + + + + +