|
@@ -1,14 +1,14 @@
|
|
|
<template>
|
|
|
<div class="sample-statistics">
|
|
|
<!-- Top: Statistics & Time Filter -->
|
|
|
- <el-row :gutter="20" class="top-bar">
|
|
|
- <el-col :span="12">
|
|
|
- <div class="stat-box">
|
|
|
- <div class="stat-title">样本统计</div>
|
|
|
- <!-- <div class="stat-value">{{ totalSamples }}</div> -->
|
|
|
- </div>
|
|
|
+ <el-row :gutter="20" class="top-bar">
|
|
|
+ <el-col :span="10">
|
|
|
+ <div style="display: flex;align-items: center;">
|
|
|
+ <span style="display: block;"> 病原体注册号:</span>
|
|
|
+ <el-input style="flex: 1;" v-model="assemblyAccession" placeholder="请输入病原体注册号"></el-input>
|
|
|
+ </div>
|
|
|
</el-col>
|
|
|
- <el-col :span="12" class="filter-col">
|
|
|
+ <el-col :span="3" class="filter-col">
|
|
|
<el-select v-model="selectedRange" placeholder="选择时间范围" @change="onRangeChange" size="small">
|
|
|
<el-option label="近24小时" value="1"></el-option>
|
|
|
<el-option label="近3天(72小时)" value="2"></el-option>
|
|
@@ -16,6 +16,9 @@
|
|
|
<el-option label="近30天" value="4"></el-option>
|
|
|
</el-select>
|
|
|
</el-col>
|
|
|
+ <el-col :span="6">
|
|
|
+ <el-button type="primary" @click="fetchData">查询</el-button>
|
|
|
+ </el-col>
|
|
|
</el-row>
|
|
|
|
|
|
<!-- Middle: Line Chart -->
|
|
@@ -23,28 +26,37 @@
|
|
|
<div ref="lineChart" class="line-chart"></div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- Bottom: Pie Charts -->
|
|
|
- <el-row :gutter="20" class="pie-row">
|
|
|
- <el-col :span="12">
|
|
|
- <div class="pie-title">样本类型占比</div>
|
|
|
- <div ref="typePieChart" class="pie-chart"></div>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <div class="pie-title">检出物占比</div>
|
|
|
- <div ref="detectedPieChart" class="pie-chart"></div>
|
|
|
+ <!-- 动态图表 -->
|
|
|
+ <el-row>
|
|
|
+ <el-col :span="12" class="pie-row">
|
|
|
+ <div style="display: flex;align-items: center;">
|
|
|
+ <div>诊断统计设置编号:</div>
|
|
|
+ <el-select style="flex: 1;" v-model="value" multiple placeholder="请选择">
|
|
|
+ <el-option
|
|
|
+ v-for="item in options"
|
|
|
+ :key="item.value"
|
|
|
+ :label="item.label"
|
|
|
+ :value="item.value">
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
</el-col>
|
|
|
- </el-row>
|
|
|
+ </el-row>
|
|
|
+ <div class="chart-section dynamic-charts">
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import * as echarts from 'echarts';
|
|
|
-import {getjcwzbInfo, getyblxtjInfo, getybtjInfo} from '@/api/statistics/report';
|
|
|
+import * as Plotly from 'plotly.js-dist-min';
|
|
|
+import {getjcwzbInfo, getyblxtjInfo, getybtjInfo,getConfigList,getConfigData} from '@/api/statistics/report';
|
|
|
export default {
|
|
|
name: 'SampleStatistics',
|
|
|
data() {
|
|
|
return {
|
|
|
selectedRange: '1',
|
|
|
+ assemblyAccession: '',
|
|
|
totalSamples: 1234,
|
|
|
lineChart: null,
|
|
|
typePieChart: null,
|
|
@@ -52,144 +64,500 @@ export default {
|
|
|
lineData: [],
|
|
|
typePieData: [],
|
|
|
detectedPieData: [],
|
|
|
+ value: [],
|
|
|
+ options: [],
|
|
|
+ dynamicChartContainers: [],
|
|
|
+ isGettingConfigList:false // 新增:标记是否正在获取配置列表
|
|
|
}
|
|
|
},
|
|
|
mounted() {
|
|
|
- this.initCharts()
|
|
|
- this.fetchData()
|
|
|
+ this.lineChart = echarts.init(this.$refs.lineChart);
|
|
|
+ // this.fetchData()
|
|
|
+ },
|
|
|
+ // 新增监听 value 变化
|
|
|
+ watch: {
|
|
|
+ value: {
|
|
|
+ handler(newValue, oldValue) {
|
|
|
+ // 判断 newValue 和 oldValue 是否相同
|
|
|
+ if (this.isArrayEqual(newValue, oldValue)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (newValue.length > 0 && this.assemblyAccession && !this.isGettingConfigList) {
|
|
|
+ this.getChartsByValue();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // deep: true
|
|
|
+ }
|
|
|
},
|
|
|
methods: {
|
|
|
- onRangeChange() {
|
|
|
- this.fetchData()
|
|
|
- },
|
|
|
- async fetchData() {
|
|
|
- const response = await getjcwzbInfo(this.selectedRange)
|
|
|
- if (response.data ) {
|
|
|
- let arr = []
|
|
|
- for(let k in response.data) {
|
|
|
- arr.push({
|
|
|
- name: k,
|
|
|
- value: response.data[k]
|
|
|
- })
|
|
|
- }
|
|
|
- this.detectedPieData = [...arr];
|
|
|
+ // 新增:判断两个数组是否相等的方法
|
|
|
+ isArrayEqual(arr1, arr2) {
|
|
|
+ if (arr1.length !== arr2.length) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 对数组进行排序后再比较
|
|
|
+ const sortedArr1 = [...arr1].sort();
|
|
|
+ const sortedArr2 = [...arr2].sort();
|
|
|
+ for (let i = 0; i < sortedArr1.length; i++) {
|
|
|
+ if (sortedArr1[i] !== sortedArr2[i]) {
|
|
|
+ return false;
|
|
|
}
|
|
|
- const res = await getyblxtjInfo(this.selectedRange)
|
|
|
- if (res.data ) {
|
|
|
- let arr = []
|
|
|
- for(let k in res.data) {
|
|
|
- arr.push({
|
|
|
- name: k,
|
|
|
- value: res.data[k]
|
|
|
- })
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ async getAllConfigList() {
|
|
|
+ this.isGettingConfigList = true; // 设置标记为正在获取
|
|
|
+ const res = await getConfigList();
|
|
|
+ this.options = res.data.map(item => ({
|
|
|
+ label: item.keyword,
|
|
|
+ value: item.id,
|
|
|
+ ...item
|
|
|
+ }));
|
|
|
+ this.value = res.data
|
|
|
+ .map(item => (item.defaultShow === 'Y' ? item.id : ''))
|
|
|
+ .filter(Boolean);
|
|
|
+ this.getChartsByValue();
|
|
|
+
|
|
|
+ },
|
|
|
+ async getChartsByValue() {
|
|
|
+ this.clearDynamicCharts();
|
|
|
+ const requests = this.value.map(async item => {
|
|
|
+ const optionItem = this.options.find(opt => opt.value === item);
|
|
|
+ if (optionItem) {
|
|
|
+ const res = await getConfigData(
|
|
|
+ this.selectedRange,
|
|
|
+ this.assemblyAccession,
|
|
|
+ item
|
|
|
+ );
|
|
|
+ if (optionItem.isNum === 'Y') {
|
|
|
+ this.renderViolinChart(res.data, optionItem.label);
|
|
|
+ } else {
|
|
|
+ this.renderLineChart(res.data, optionItem.label);
|
|
|
}
|
|
|
- this.typePieData = [...arr];
|
|
|
}
|
|
|
- const result = await getybtjInfo(this.selectedRange)
|
|
|
- const analysisData = []
|
|
|
- const adoptionData = []
|
|
|
- // 生成日期
|
|
|
- const xData = []
|
|
|
- if (result.data ) {
|
|
|
- result.data.forEach(item => {
|
|
|
- analysisData.push(item.fxl)
|
|
|
- adoptionData.push(item.ybl)
|
|
|
- xData.push(item.hour)
|
|
|
- })
|
|
|
- }
|
|
|
- // 模拟数据,根据selectedRange更新
|
|
|
- // const now = new Date()
|
|
|
- // let days = 1
|
|
|
- // if (this.selectedRange === '2') days = 3
|
|
|
- // if (this.selectedRange === '3') days = 7
|
|
|
- // if (this.selectedRange === '4') days = 30
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- // for (let i = days - 1; i >= 0; i--) {
|
|
|
- // const d = new Date(now)
|
|
|
- // d.setDate(now.getDate() - i)
|
|
|
- // xData.push(`${d.getMonth() + 1}-${d.getDate()}`)
|
|
|
- // analysisData.push(Math.floor(Math.random() * 100 + 100))
|
|
|
- // adoptionData.push(Math.floor(Math.random() * 80 + 50))
|
|
|
- // }
|
|
|
- this.lineData = { xData, analysisData, adoptionData }
|
|
|
-
|
|
|
- // // 饼图数据
|
|
|
- // this.typePieData = [
|
|
|
- // { value: 335, name: '血液' },
|
|
|
- // { value: 310, name: '尿液' },
|
|
|
- // { value: 234, name: '唾液' },
|
|
|
- // { value: 135, name: '其他' }
|
|
|
- // ]
|
|
|
- // this.detectedPieData = [
|
|
|
- // { value: 400, name: '物质A' },
|
|
|
- // { value: 335, name: '物质B' },
|
|
|
- // { value: 310, name: '物质C' },
|
|
|
- // { value: 234, name: '其他' }
|
|
|
- // ]
|
|
|
- // this.totalSamples = analysisData.reduce((a, b) => a + b, 0)
|
|
|
- this.updateCharts()
|
|
|
+ });
|
|
|
+
|
|
|
+ await Promise.all(requests);
|
|
|
+
|
|
|
+ this.isGettingConfigList = false; // 重置标记为获取完成
|
|
|
},
|
|
|
- initCharts() {
|
|
|
- this.lineChart = echarts.init(this.$refs.lineChart)
|
|
|
- this.typePieChart = echarts.init(this.$refs.typePieChart)
|
|
|
- this.detectedPieChart = echarts.init(this.$refs.detectedPieChart)
|
|
|
+ renderViolinChart(data, label) {
|
|
|
+ const chartContainer = document.createElement('div');
|
|
|
+ chartContainer.style.width = '100%';
|
|
|
+ chartContainer.style.height = '600px';
|
|
|
+ chartContainer.id = `violinChart_${label}`;
|
|
|
+ chartContainer.classList.add('dynamic-chart');
|
|
|
+ document.querySelector('.dynamic-charts').appendChild(chartContainer);
|
|
|
+
|
|
|
+ // 新增:将容器添加到数组中
|
|
|
+ this.dynamicChartContainers.push(chartContainer);
|
|
|
+
|
|
|
+ const traces = data.map((item, index) => {
|
|
|
+ let name;
|
|
|
+ if (typeof item.hour === 'number') {
|
|
|
+ name = `${item.hour}小时`;
|
|
|
+ } else {
|
|
|
+ name = item.hour;
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ type: 'violin',
|
|
|
+ y: item.y,
|
|
|
+ name,
|
|
|
+ box: {
|
|
|
+ visible: true,
|
|
|
+ line: {
|
|
|
+ color: '#333',
|
|
|
+ width: 1.5
|
|
|
+ }
|
|
|
+ },
|
|
|
+ meanline: {
|
|
|
+ visible: true,
|
|
|
+ color: '#ff4d4f'
|
|
|
+ },
|
|
|
+ line: {
|
|
|
+ color: `hsl(${index * 360 / data.length}, 70%, 50%)`
|
|
|
+ },
|
|
|
+ fillcolor: `hsla(${index * 360 / data.length}, 70%, 50%, 0.2)`,
|
|
|
+ opacity: 0.8
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ const layout = {
|
|
|
+ title: {
|
|
|
+ text: `提琴图 - ${label}`,
|
|
|
+ font: {
|
|
|
+ size: 20,
|
|
|
+ color: '#333'
|
|
|
+ },
|
|
|
+ y: 0.95
|
|
|
+ },
|
|
|
+ yaxis: {
|
|
|
+ zeroline: false,
|
|
|
+ title: {
|
|
|
+ text: '数值',
|
|
|
+ font: {
|
|
|
+ size: 16,
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ tickfont: {
|
|
|
+ size: 14,
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ xaxis: {
|
|
|
+ title: {
|
|
|
+ text: typeof data[0]?.hour === 'number' ? '小时' : '日期',
|
|
|
+ font: {
|
|
|
+ size: 16,
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ tickfont: {
|
|
|
+ size: 14,
|
|
|
+ color: '#666'
|
|
|
+ },
|
|
|
+ tickangle: -45
|
|
|
+ },
|
|
|
+ violinmode: 'group',
|
|
|
+ margin: {
|
|
|
+ l: 80,
|
|
|
+ r: 80,
|
|
|
+ b: 120,
|
|
|
+ t: 120,
|
|
|
+ pad: 10
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ font: {
|
|
|
+ size: 14,
|
|
|
+ color: '#666'
|
|
|
+ },
|
|
|
+ orientation: 'h',
|
|
|
+ yanchor: 'top',
|
|
|
+ y: -0.2,
|
|
|
+ xanchor: 'center',
|
|
|
+ x: 0.5
|
|
|
+ },
|
|
|
+ plot_bgcolor: '#fff',
|
|
|
+ paper_bgcolor: '#fff'
|
|
|
+ };
|
|
|
+
|
|
|
+ Plotly.newPlot(chartContainer, traces, layout);
|
|
|
},
|
|
|
- updateCharts() {
|
|
|
- // 折线图
|
|
|
- this.lineChart.setOption({
|
|
|
- tooltip: { trigger: 'axis' },
|
|
|
- legend: { data: ['数据分析量', '收样量'] },
|
|
|
- xAxis: { type: 'category', data: this.lineData.xData },
|
|
|
- yAxis: { type: 'value', name: '数量' },
|
|
|
+
|
|
|
+ renderLineChart(data, label) {
|
|
|
+ const chartContainer = document.createElement('div');
|
|
|
+ chartContainer.style.width = '100%';
|
|
|
+ chartContainer.style.height = '600px';
|
|
|
+ chartContainer.id = `lineChart_${label}`;
|
|
|
+ chartContainer.classList.add('dynamic-chart');
|
|
|
+ document.querySelector('.dynamic-charts').appendChild(chartContainer);
|
|
|
+
|
|
|
+ // 新增:将容器添加到数组中
|
|
|
+ this.dynamicChartContainers.push(chartContainer);
|
|
|
+
|
|
|
+ const chart = echarts.init(chartContainer);
|
|
|
+ const xData = data.map(item => {
|
|
|
+ if (typeof item.hour === 'number') {
|
|
|
+ return `${item.hour}小时`;
|
|
|
+ }
|
|
|
+ return item.hour;
|
|
|
+ });
|
|
|
+ const xdsData = data.map(item => item.xds);
|
|
|
+ const xdsyxData = data.map(item => item.xdsyx);
|
|
|
+
|
|
|
+ const option = {
|
|
|
+ title: {
|
|
|
+ text: `折线图 - ${label}`,
|
|
|
+ left: 'center',
|
|
|
+ top: '3%',
|
|
|
+ textStyle: {
|
|
|
+ color: '#333',
|
|
|
+ fontSize: 20
|
|
|
+ }
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
|
|
|
+ textStyle: {
|
|
|
+ color: '#fff'
|
|
|
+ },
|
|
|
+ axisPointer: {
|
|
|
+ type: 'cross',
|
|
|
+ crossStyle: {
|
|
|
+ color: '#999'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ top: '10%',
|
|
|
+ textStyle: {
|
|
|
+ color: '#666',
|
|
|
+ fontSize: 14
|
|
|
+ }
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: xData,
|
|
|
+ axisLabel: {
|
|
|
+ color: '#666',
|
|
|
+ fontSize: 14,
|
|
|
+ rotate: 45
|
|
|
+ },
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisTick: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ name: typeof data[0]?.hour === 'number' ? '小时' : '日期'
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ name: '数值',
|
|
|
+ nameTextStyle: {
|
|
|
+ color: '#666',
|
|
|
+ fontSize: 16
|
|
|
+ },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#666',
|
|
|
+ fontSize: 14
|
|
|
+ },
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ axisTick: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ splitLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#eee'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
series: [
|
|
|
{
|
|
|
- name: '数据分析量',
|
|
|
+ name: 'xds',
|
|
|
type: 'line',
|
|
|
+ data: xdsData,
|
|
|
smooth: true,
|
|
|
- data: this.lineData.analysisData
|
|
|
+ lineStyle: {
|
|
|
+ color: '#409EFF',
|
|
|
+ width: 2
|
|
|
+ },
|
|
|
+ symbol: 'circle',
|
|
|
+ symbolSize: 8,
|
|
|
+ itemStyle: {
|
|
|
+ color: '#409EFF'
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ symbolSize: 12
|
|
|
+ }
|
|
|
},
|
|
|
{
|
|
|
- name: '收样量',
|
|
|
+ name: 'xdsyx',
|
|
|
type: 'line',
|
|
|
+ data: xdsyxData,
|
|
|
smooth: true,
|
|
|
- data: this.lineData.adoptionData
|
|
|
+ lineStyle: {
|
|
|
+ color: '#E6A23C',
|
|
|
+ width: 2
|
|
|
+ },
|
|
|
+ symbol: 'circle',
|
|
|
+ symbolSize: 8,
|
|
|
+ itemStyle: {
|
|
|
+ color: '#E6A23C'
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ symbolSize: 12
|
|
|
+ }
|
|
|
}
|
|
|
]
|
|
|
- })
|
|
|
- // 样本类型饼图
|
|
|
- this.typePieChart.setOption({
|
|
|
- tooltip: { trigger: 'item' },
|
|
|
- legend: { bottom: 0 },
|
|
|
- series: [
|
|
|
+ };
|
|
|
+
|
|
|
+ chart.setOption(option);
|
|
|
+ },
|
|
|
+
|
|
|
+ onRangeChange() {
|
|
|
+ this.fetchData()
|
|
|
+ },
|
|
|
+ async fetchData() {
|
|
|
+ if (!this.assemblyAccession) {
|
|
|
+ this.$message.error('请输入病原体注册号');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.getAllConfigList();
|
|
|
+ const result = await getybtjInfo(this.selectedRange, this.assemblyAccession);
|
|
|
+ this.processData(result.data);
|
|
|
+ this.updateCharts();
|
|
|
+ },
|
|
|
+ clearDynamicCharts() {
|
|
|
+ this.dynamicChartContainers.forEach(container => {
|
|
|
+ // 移除图表容器
|
|
|
+ container.remove();
|
|
|
+ });
|
|
|
+ // 清空数组
|
|
|
+ this.dynamicChartContainers = [];
|
|
|
+ },
|
|
|
+ processData(data) {
|
|
|
+ const groupedData = {};
|
|
|
+ data.forEach(item => {
|
|
|
+ if (!groupedData[item.hour]) {
|
|
|
+ groupedData[item.hour] = { yxs: 0, yxl: 0 };
|
|
|
+ }
|
|
|
+ groupedData[item.hour].yxs += item.yxs;
|
|
|
+ groupedData[item.hour].yxl += item.yxl;
|
|
|
+ });
|
|
|
+ this.lineData = Object.keys(groupedData).map(hour => ({
|
|
|
+ hour, // 直接使用原始的日期字符串
|
|
|
+ yxs: groupedData[hour].yxs,
|
|
|
+ yxl: groupedData[hour].yxl
|
|
|
+ })).sort((a, b) => new Date(a.hour) - new Date(b.hour)); // 按日期排序
|
|
|
+ },
|
|
|
+ updateCharts() {
|
|
|
+ const xData = this.lineData.map(item => item.hour);
|
|
|
+ const yxsData = this.lineData.map(item => item.yxs);
|
|
|
+ const yxlData = this.lineData.map(item => item.yxl);
|
|
|
+
|
|
|
+ const option = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ axisPointer: {
|
|
|
+ type: 'cross',
|
|
|
+ crossStyle: {
|
|
|
+ color: '#999'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 格式化提示框内容
|
|
|
+ formatter: function (params) {
|
|
|
+ // let result = `${params[0].name}<br/>`;
|
|
|
+ let result = '';
|
|
|
+ params.forEach(param => {
|
|
|
+ result += `${param.seriesName}: ${param.value}<br/>`;
|
|
|
+ });
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ data: ['阳性占比', '阳性率'],
|
|
|
+ show: true,
|
|
|
+ // 调整图例位置
|
|
|
+ top: '5%'
|
|
|
+ },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category', // 依然使用 category 类型
|
|
|
+ data: xData,
|
|
|
+ axisLabel: {
|
|
|
+ // 格式化日期显示
|
|
|
+ formatter: function (value) {
|
|
|
+ return value;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 美化 X 轴轴线
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 美化 X 轴刻度线
|
|
|
+ axisTick: {
|
|
|
+ show: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ yAxis: [
|
|
|
{
|
|
|
- name: '样本类型',
|
|
|
- type: 'pie',
|
|
|
- radius: '60%',
|
|
|
- data: this.typePieData,
|
|
|
- emphasis: {
|
|
|
- itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' }
|
|
|
+ type: 'value',
|
|
|
+ name: '阳性占比',
|
|
|
+ position: 'left',
|
|
|
+ axisLabel: {
|
|
|
+ formatter: '{value}'
|
|
|
+ },
|
|
|
+ // 美化 Y 轴轴线
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 美化 Y 轴刻度线
|
|
|
+ axisTick: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ // 美化 Y 轴网格线
|
|
|
+ splitLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#eee'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'value',
|
|
|
+ name: '阳性率',
|
|
|
+ position: 'right',
|
|
|
+ axisLabel: {
|
|
|
+ formatter: '{value}'
|
|
|
+ },
|
|
|
+ // 美化 Y 轴轴线
|
|
|
+ axisLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 美化 Y 轴刻度线
|
|
|
+ axisTick: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ // 美化 Y 轴网格线
|
|
|
+ splitLine: {
|
|
|
+ lineStyle: {
|
|
|
+ color: '#eee'
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- ]
|
|
|
- })
|
|
|
- // 检出物饼图
|
|
|
- this.detectedPieChart.setOption({
|
|
|
- tooltip: { trigger: 'item' },
|
|
|
- legend: { bottom: 0 },
|
|
|
+ ],
|
|
|
series: [
|
|
|
{
|
|
|
- name: '检出物',
|
|
|
- type: 'pie',
|
|
|
- radius: '60%',
|
|
|
- data: this.detectedPieData,
|
|
|
+ name: '阳性占比',
|
|
|
+ type: 'bar',
|
|
|
+ data: yxsData,
|
|
|
+ // 更换柱状图颜色
|
|
|
+ itemStyle: {
|
|
|
+ color: '#91cc75'
|
|
|
+ },
|
|
|
+ // 调整柱状图宽度
|
|
|
+ barWidth: '40%'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '阳性率',
|
|
|
+ type: 'line',
|
|
|
+ yAxisIndex: 1,
|
|
|
+ data: yxlData,
|
|
|
+ // 让折线更圆滑
|
|
|
+ smooth: true,
|
|
|
+ // 更换折线颜色
|
|
|
+ lineStyle: {
|
|
|
+ color: '#fac858'
|
|
|
+ },
|
|
|
+ // 显示折线数据点
|
|
|
+ symbol: 'circle',
|
|
|
+ // 数据点大小
|
|
|
+ symbolSize: 8,
|
|
|
+ // 数据点颜色
|
|
|
+ itemStyle: {
|
|
|
+ color: '#fac858'
|
|
|
+ },
|
|
|
+ // 鼠标悬停时数据点变大
|
|
|
emphasis: {
|
|
|
- itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' }
|
|
|
+ symbolSize: 12
|
|
|
}
|
|
|
}
|
|
|
]
|
|
|
- })
|
|
|
+ };
|
|
|
+
|
|
|
+ this.lineChart.setOption(option);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -244,4 +612,10 @@ export default {
|
|
|
margin-bottom: 8px;
|
|
|
color: #333;
|
|
|
}
|
|
|
+.dynamic-chart {
|
|
|
+ margin-bottom: 24px;
|
|
|
+ border: 1px solid #eee;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
|
+}
|
|
|
</style>
|