
# Conflicts: # src/api/manage.js # src/views/spectrumAnalysis/gamma-analysis.vue # src/views/spectrumAnalysis/index.vue
1269 lines
36 KiB
Vue
1269 lines
36 KiB
Vue
<template>
|
||
<div class="gamma-analysis">
|
||
<a-spin :spinning="isLoading">
|
||
<!-- 二级交互栏 -->
|
||
<div class="spectrum-analysis-sub-operators">
|
||
<pop-over-with-icon placement="bottomLeft">
|
||
Detailed-Information
|
||
<detailed-infomation slot="content" :data="detailedInfomation" />
|
||
</pop-over-with-icon>
|
||
<pop-over-with-icon placement="bottomLeft">
|
||
QC Flags
|
||
<qc-flags slot="content" :data="qcFlags" />
|
||
</pop-over-with-icon>
|
||
<pop-over-with-icon>
|
||
Graph Assistance
|
||
<graph-assistance
|
||
v-if="!isLoading"
|
||
slot="content"
|
||
@change="handleGraphAssistanceChange"
|
||
@reset="handleReset"
|
||
/>
|
||
</pop-over-with-icon>
|
||
<pop-over-with-icon>
|
||
Nuclide Library
|
||
<nuclear-library slot="content" :list="nuclideLibraryList" />
|
||
</pop-over-with-icon>
|
||
<div class="peak-info">
|
||
<button-with-switch-icon @change="handlePeakInfoChange" @click="handleTogglePeak"></button-with-switch-icon>
|
||
</div>
|
||
</div>
|
||
<!-- 二级交互栏结束 -->
|
||
<!-- 主体部分 -->
|
||
<div class="gamma-analysis-main">
|
||
<div class="gamma-analysis-chart">
|
||
<CustomChart
|
||
ref="chartRef"
|
||
:option="option"
|
||
:opts="opts"
|
||
@zr:click="handleChartClick"
|
||
@zr:mousedown="handleMouseDown"
|
||
@zr:mouseup="handleMouseUp"
|
||
@brushEnd="handleBrushEnd"
|
||
style="height: 100%"
|
||
/>
|
||
<!-- 右上角缩略图 -->
|
||
<div class="gamma-analysis-thumbnail">
|
||
<CustomChart
|
||
ref="thumbnailChartRef"
|
||
:option="thumbnailOption"
|
||
@zr:click="handleThumbnailChartClick"
|
||
style="height: 100%"
|
||
/>
|
||
</div>
|
||
<!-- 缩略图结束 -->
|
||
|
||
<!-- 自定义tooltip,用于展示Peak Infomation -->
|
||
<div
|
||
v-if="peakInfomationTooltip.visible"
|
||
class="peak-infomation-tooltip"
|
||
:style="{
|
||
top: peakInfomationTooltip.top + 'px',
|
||
left: peakInfomationTooltip.left + 'px',
|
||
}"
|
||
>
|
||
<div class="peak-infomation-tooltip-content" v-html="peakInfomationTooltip.content"></div>
|
||
</div>
|
||
<!-- tooltip结束 -->
|
||
</div>
|
||
</div>
|
||
<!-- 主体部分结束 -->
|
||
</a-spin>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import CustomChart from '@/components/CustomChart/index.vue'
|
||
import PopOverWithIcon from './components/SubOperators/PopOverWithIcon.vue'
|
||
import DetailedInfomation from './components/SubOperators/DetailedInfomation.vue'
|
||
import QcFlags from './components/SubOperators/QcFlags.vue'
|
||
import GraphAssistance from './components/SubOperators/GraphAssistance.vue'
|
||
import NuclearLibrary from './components/SubOperators/NuclearLibrary.vue'
|
||
import ButtonWithSwitchIcon from './components/SubOperators/ButtonWithSwitchIcon.vue'
|
||
import { getAction } from '@/api/manage'
|
||
import Response from './response.json'
|
||
import { buildLineSeries, findSeriesByName, getXAxisAndYAxisByPosition, rangeNumber } from '@/utils/chartHelper'
|
||
import { cloneDeep } from 'lodash'
|
||
import axios from 'axios'
|
||
|
||
// 初始配置
|
||
const initialOption = {
|
||
grid: {
|
||
top: 40,
|
||
left: 60,
|
||
right: 50,
|
||
containLabel: true,
|
||
},
|
||
title: {
|
||
text: '',
|
||
left: 'center',
|
||
bottom: 10,
|
||
textStyle: {
|
||
color: '#8FD4F8',
|
||
rich: {
|
||
a: {
|
||
padding: [0, 20, 0, 0],
|
||
fontSize: 16,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
axisPointer: {
|
||
lineStyle: {
|
||
color: '#3CAEBB',
|
||
width: 1,
|
||
type: 'solid',
|
||
},
|
||
},
|
||
formatter: undefined,
|
||
className: 'figure-chart-option-tooltip',
|
||
},
|
||
xAxis: {
|
||
name: 'Channel',
|
||
nameTextStyle: {
|
||
color: '#8FD4F8',
|
||
fontSize: 16,
|
||
align: 'right',
|
||
verticalAlign: 'top',
|
||
padding: [30, 0, 0, 0],
|
||
},
|
||
axisLine: {
|
||
lineStyle: {
|
||
color: '#ade6ee',
|
||
},
|
||
},
|
||
splitLine: {
|
||
show: false,
|
||
},
|
||
axisLabel: {
|
||
textStyle: {
|
||
color: '#ade6ee',
|
||
},
|
||
},
|
||
min: 1,
|
||
max: 'dataMax',
|
||
animation: false,
|
||
axisLabel: {
|
||
formatter: (value) => {
|
||
return parseInt(value)
|
||
},
|
||
},
|
||
},
|
||
yAxis: {
|
||
name: 'Counts',
|
||
type: 'value',
|
||
nameTextStyle: {
|
||
color: '#8FD4F8',
|
||
fontSize: 16,
|
||
},
|
||
axisLine: {
|
||
show: true,
|
||
lineStyle: {
|
||
color: '#ade6ee',
|
||
},
|
||
},
|
||
splitLine: {
|
||
show: true,
|
||
lineStyle: {
|
||
color: 'rgba(173, 230, 238, .2)',
|
||
},
|
||
},
|
||
axisLabel: {
|
||
textStyle: {
|
||
color: '#ade6ee',
|
||
},
|
||
},
|
||
min: 1,
|
||
max: 'dataMax',
|
||
animation: false,
|
||
axisLabel: {
|
||
formatter: (value) => {
|
||
return value.toFixed(1)
|
||
},
|
||
},
|
||
},
|
||
series: [],
|
||
brush: {},
|
||
}
|
||
|
||
// 缩略图配置
|
||
const thumbnailOption = {
|
||
grid: {
|
||
top: 0,
|
||
left: 5,
|
||
right: 5,
|
||
bottom: 0,
|
||
},
|
||
xAxis: {
|
||
type: 'category',
|
||
axisLine: {
|
||
show: false,
|
||
},
|
||
splitLine: {
|
||
show: false,
|
||
},
|
||
axisLabel: {
|
||
show: false,
|
||
},
|
||
min: 1,
|
||
max: 'dataMax',
|
||
},
|
||
yAxis: {
|
||
type: 'value',
|
||
axisLine: {
|
||
show: false,
|
||
},
|
||
splitLine: {
|
||
show: false,
|
||
},
|
||
axisLabel: {
|
||
show: false,
|
||
},
|
||
min: 1,
|
||
max: 'dataMax',
|
||
},
|
||
series: [],
|
||
}
|
||
|
||
const graphAssistance = {
|
||
axisType: 'Channel',
|
||
Baseline: true,
|
||
SCAC: true,
|
||
Lc: true,
|
||
}
|
||
|
||
export default {
|
||
props: {
|
||
sample: {
|
||
type: Object,
|
||
},
|
||
},
|
||
components: {
|
||
CustomChart,
|
||
PopOverWithIcon,
|
||
DetailedInfomation,
|
||
QcFlags,
|
||
GraphAssistance,
|
||
NuclearLibrary,
|
||
ButtonWithSwitchIcon,
|
||
},
|
||
data() {
|
||
return {
|
||
isLoading: false,
|
||
option: cloneDeep(initialOption),
|
||
opts: {
|
||
notMerge: false,
|
||
},
|
||
thumbnailOption: cloneDeep(thumbnailOption),
|
||
|
||
detailedInfomation: [],
|
||
qcFlags: [],
|
||
graphAssistance: cloneDeep(graphAssistance),
|
||
|
||
channelPeakGroup: [],
|
||
energyPeakGroup: [],
|
||
|
||
nuclideLibraryList: [], // 当前鼠标点击选中的channel
|
||
peakInfomationTooltip: {
|
||
// Peak Infomation的位置
|
||
visible: false,
|
||
content: '',
|
||
top: 0,
|
||
left: 0,
|
||
},
|
||
}
|
||
},
|
||
created() {
|
||
this.option.title.text = '{a|Channel:0} {a|Energy:0} {a|Counts:0} {a|Detectability:0}'
|
||
this.$bus.$on('colorChange', this.handleColorChange)
|
||
this.$bus.$on('gammaRefresh', this.handleRefresh)
|
||
this.$bus.$on('accept', this.handleAccept)
|
||
},
|
||
destroyed() {
|
||
this.$bus.$off('colorChange', this.handleColorChange)
|
||
this.$bus.$off('gammaRefresh', this.handleRefresh)
|
||
this.$bus.$off('accept', this.handleAccept)
|
||
},
|
||
mounted() {
|
||
this.option.brush = { toolbox: [] }
|
||
},
|
||
methods: {
|
||
async getSampleDetail() {
|
||
const { dbName, sampleId } = this.sample
|
||
try {
|
||
this.isLoading = true
|
||
|
||
this.reset()
|
||
|
||
// const { success, result, message } = Response
|
||
|
||
if (this._cancelToken && typeof this._cancelToken == 'function') {
|
||
this._cancelToken()
|
||
}
|
||
|
||
const cancelToken = new axios.CancelToken((c) => {
|
||
this._cancelToken = c
|
||
})
|
||
|
||
const { success, result, message } = await getAction(
|
||
'/gamma/gammaByDB',
|
||
{
|
||
dbName,
|
||
sampleId,
|
||
},
|
||
cancelToken
|
||
)
|
||
console.log('%c [ result ]-243', 'font-size:13px; background:pink; color:#bf2c9f;', result)
|
||
if (success) {
|
||
this.dataProsess(result)
|
||
} else {
|
||
this.$message.error(message)
|
||
}
|
||
} catch (error) {
|
||
console.error(error)
|
||
}
|
||
},
|
||
|
||
async getSampleDetail_file() {
|
||
const { inputFileName: fileName } = this.sample
|
||
try {
|
||
this.isLoading = true
|
||
this.option.series = []
|
||
this.thumbnailOption.series = []
|
||
// const { success, result, message } = Response
|
||
const { success, result, message } = await getAction('/gamma/gammaByFile', {
|
||
fileName,
|
||
})
|
||
console.log('%c [ result ]-243', 'font-size:13px; background:pink; color:#bf2c9f;', result)
|
||
if (success) {
|
||
this.dataProsess(result)
|
||
} else {
|
||
this.$message.error(message)
|
||
}
|
||
} catch (error) {
|
||
console.error(error)
|
||
}
|
||
},
|
||
|
||
dataProsess(result) {
|
||
this.isLoading = false
|
||
|
||
const {
|
||
dead_time,
|
||
live_time,
|
||
real_time,
|
||
start_time,
|
||
|
||
DetailedInformation,
|
||
QCFlag,
|
||
|
||
allData,
|
||
|
||
shadowChannelChart,
|
||
shadowEnergyChart,
|
||
|
||
shapeChannelData,
|
||
shapeEnergyData,
|
||
} = result
|
||
|
||
this.detailedInfomation = DetailedInformation
|
||
this.qcFlags = QCFlag
|
||
|
||
const channelPeakGroup = allData.filter((item) => item.name == 'Peak' && item.group == 'channel')
|
||
const energyPeakGroup = allData.filter((item) => item.name == 'Peak' && item.group == 'energy')
|
||
|
||
const channelBaseLine = allData.find((item) => item.name == 'BaseLine' && item.group == 'channel') || {}
|
||
const energyBaseLine = allData.find((item) => item.name == 'BaseLine' && item.group == 'energy')
|
||
|
||
const channelLcLine = allData.find((item) => item.name == 'Lc' && item.group == 'channel') || {}
|
||
const energyLcLine = allData.find((item) => item.name == 'Lc' && item.group == 'energy')
|
||
|
||
const channelScacLine = allData.find((item) => item.name == 'Scac' && item.group == 'channel') || {}
|
||
const energyScacLine = allData.find((item) => item.name == 'Scac' && item.group == 'energy')
|
||
|
||
this.allEnergy = allData.find((item) => item.name == 'Energy' && item.group == 'energy') || {}
|
||
this.allChannel = allData.find((item) => item.name == 'Count' && item.group == 'channel')
|
||
|
||
// 保存Peak Line
|
||
this.channelPeakGroup = channelPeakGroup
|
||
this.energyPeakGroup = energyPeakGroup
|
||
|
||
// 保存 Spectrum Line
|
||
this.shadowChannelChart = shadowChannelChart
|
||
this.shadowEnergyChart = shadowEnergyChart
|
||
|
||
// 保存基线
|
||
this.channelBaseLine = channelBaseLine
|
||
this.energyBaseLine = energyBaseLine
|
||
|
||
// 保存Lc
|
||
this.channelLcLine = channelLcLine
|
||
this.energyLcLine = energyLcLine
|
||
|
||
// 保存Scac
|
||
this.channelScacLine = channelScacLine
|
||
this.energyScacLine = energyScacLine
|
||
|
||
// 保存 基线控制点
|
||
this.shapeChannelData = shapeChannelData
|
||
this.shapeEnergyData = shapeEnergyData
|
||
|
||
this.option.yAxis.max =
|
||
shadowChannelChart.pointlist && Math.ceil(Math.max(...shadowChannelChart.pointlist.map((item) => item.y)) * 1.1)
|
||
this.thumbnailOption.yAxis.max =
|
||
shadowChannelChart.pointlist && Math.ceil(Math.max(...shadowChannelChart.pointlist.map((item) => item.y)) * 1.1)
|
||
|
||
const series = []
|
||
|
||
// 推入Spectrum Line
|
||
series.push(
|
||
buildLineSeries(
|
||
'Spectrum',
|
||
shadowChannelChart.pointlist && shadowChannelChart.pointlist.map(({ x, y }) => [x, y]),
|
||
shadowChannelChart.color,
|
||
{
|
||
markLine: {
|
||
silent: true,
|
||
symbol: 'none',
|
||
label: {
|
||
show: false,
|
||
},
|
||
lineStyle: {
|
||
color: 'red',
|
||
width: 1,
|
||
},
|
||
data: [{ xAxis: -1 }],
|
||
},
|
||
}
|
||
)
|
||
)
|
||
|
||
// 右上角缩略图推入Spectrum Line
|
||
this.thumbnailOption.series.push(
|
||
buildLineSeries(
|
||
'Spectrum',
|
||
shadowChannelChart.pointlist && shadowChannelChart.pointlist.map(({ x, y }) => [x, y]),
|
||
shadowChannelChart.color,
|
||
{
|
||
silent: true,
|
||
markLine: {
|
||
silent: true,
|
||
symbol: 'none',
|
||
label: {
|
||
show: false,
|
||
},
|
||
lineStyle: {
|
||
type: 'solid',
|
||
color: '#1397a3',
|
||
width: 1,
|
||
},
|
||
data: [],
|
||
},
|
||
}
|
||
)
|
||
)
|
||
|
||
// 推入BaseLine
|
||
series.push(
|
||
buildLineSeries(
|
||
'BaseLine',
|
||
channelBaseLine.pointlist && channelBaseLine.pointlist.map(({ x, y }) => [x, y]),
|
||
channelBaseLine.color,
|
||
{
|
||
zlevel: 2,
|
||
}
|
||
)
|
||
)
|
||
|
||
// 推入LcLine线
|
||
series.push(
|
||
buildLineSeries(
|
||
'LcLine',
|
||
channelLcLine.pointlist && channelLcLine.pointlist.map(({ x, y }) => [x, y]),
|
||
channelLcLine.color,
|
||
{
|
||
zlevel: 3,
|
||
}
|
||
)
|
||
)
|
||
|
||
// 推入Scac线
|
||
series.push(
|
||
buildLineSeries(
|
||
'ScacLine',
|
||
channelScacLine.pointlist && channelScacLine.pointlist.map(({ x, y }) => [x, y]),
|
||
channelScacLine.color,
|
||
{
|
||
zlevel: 4,
|
||
}
|
||
)
|
||
)
|
||
|
||
// 推入基线控制点
|
||
series.push({
|
||
name: 'BaseLine_Ctrl_Point',
|
||
type: 'scatter',
|
||
data: shapeChannelData.map(({ size, color, point: { x, y } }) => {
|
||
return {
|
||
value: [x, y],
|
||
itemStyle: {
|
||
color: 'transparent',
|
||
borderColor: color,
|
||
borderWidth: size / 2,
|
||
},
|
||
}
|
||
}),
|
||
emphasis: {
|
||
disabled: true,
|
||
},
|
||
animation: false,
|
||
zlevel: 5,
|
||
})
|
||
|
||
// 推入Peak Line
|
||
const peakLines = []
|
||
channelPeakGroup.forEach((item, index) => {
|
||
peakLines.push(
|
||
buildLineSeries(
|
||
`Peak_${index}`,
|
||
item.pointlist.map(({ x, y }) => [x, y]),
|
||
item.color,
|
||
{
|
||
zlevel: 6,
|
||
}
|
||
)
|
||
)
|
||
})
|
||
series.push(...peakLines)
|
||
this.option.series = series
|
||
|
||
this.option.tooltip.formatter = this.tooltipFormatter
|
||
},
|
||
|
||
tooltipFormatter(params) {
|
||
if (this.isEnergy()) {
|
||
const energy = params[0].value[0]
|
||
const channel = this.getChannelByEnergy(energy)
|
||
|
||
return `<div class="channel">Channel: ${channel}</div>
|
||
<div class="energy">Energy: ${energy.toFixed(2)}</div>`
|
||
} else {
|
||
const channel = parseInt(params[0].value[0].toFixed())
|
||
const energy = this.allEnergy.pointlist && this.allEnergy.pointlist[channel - 1]
|
||
return energy
|
||
? `<div class="channel">Channel: ${channel}</div>
|
||
<div class="energy">Energy: ${energy.x.toFixed(2)}</div>`
|
||
: undefined
|
||
}
|
||
},
|
||
|
||
// Graph Assistance 操作
|
||
handleGraphAssistanceChange({ type, label, value }) {
|
||
// 类型变化
|
||
if (type == 'labelChange') {
|
||
switch (label) {
|
||
case 'Linear':
|
||
this.option.yAxis.type = 'value'
|
||
this.thumbnailOption.yAxis.type = 'value'
|
||
this.handleReset()
|
||
break
|
||
case 'Log10':
|
||
this.option.yAxis.type = 'log'
|
||
this.thumbnailOption.yAxis.type = 'log'
|
||
this.handleReset()
|
||
break
|
||
case 'Channel':
|
||
case 'Energy':
|
||
this.graphAssistance.axisType = label
|
||
this.option.xAxis.name = label
|
||
|
||
this.handleReset()
|
||
|
||
this.redrawLineBySeriesName(
|
||
'BaseLine',
|
||
this.energyBaseLine,
|
||
this.channelBaseLine,
|
||
this.graphAssistance.Baseline
|
||
)
|
||
this.redrawLineBySeriesName('LcLine', this.energyLcLine, this.channelLcLine, this.graphAssistance.Lc)
|
||
this.redrawLineBySeriesName(
|
||
'ScacLine',
|
||
this.energyScacLine,
|
||
this.channelScacLine,
|
||
this.graphAssistance.SCAC
|
||
)
|
||
|
||
this.redrawLineBySeriesName('Spectrum', this.shadowEnergyChart, this.shadowChannelChart)
|
||
this.redrawCtrlPointBySeriesName()
|
||
this.redrawPeakLine()
|
||
|
||
this.redrawThumbnailChart()
|
||
break
|
||
case 'Lines':
|
||
this.option.series[0].type = 'line'
|
||
this.option.series[0].symbol = 'none'
|
||
|
||
this.thumbnailOption.series[0].type = 'line'
|
||
this.thumbnailOption.series[0].symbol = 'none'
|
||
break
|
||
case 'Scatter':
|
||
this.option.series[0].type = 'scatterGL'
|
||
this.option.series[0].symbol = 'circle'
|
||
|
||
this.thumbnailOption.series[0].type = 'scatterGL'
|
||
this.thumbnailOption.series[0].symbol = 'circle'
|
||
break
|
||
}
|
||
}
|
||
// 值变化
|
||
else if (type == 'valueChange') {
|
||
this.graphAssistance[label] = value
|
||
|
||
switch (label) {
|
||
case 'Cursor':
|
||
// 显示红色竖线
|
||
if (value) {
|
||
this.option.series[0].markLine.lineStyle.width = 2
|
||
} else {
|
||
this.option.series[0].markLine.lineStyle.width = 0
|
||
}
|
||
break
|
||
case 'Baseline':
|
||
this.redrawLineBySeriesName('BaseLine', this.energyBaseLine, this.channelBaseLine, value)
|
||
break
|
||
case 'Lc':
|
||
this.redrawLineBySeriesName('LcLine', this.energyLcLine, this.channelLcLine, value)
|
||
break
|
||
case 'SCAC':
|
||
this.redrawLineBySeriesName('ScacLine', this.energyScacLine, this.channelScacLine, value)
|
||
break
|
||
}
|
||
}
|
||
},
|
||
|
||
// 根据seriesName重绘线
|
||
redrawLineBySeriesName(seriesName, energyData, channelData, isShow = true) {
|
||
const series = findSeriesByName(this.option.series, seriesName)
|
||
if (isShow) {
|
||
const data = this.isEnergy() ? energyData : channelData
|
||
series.data = data.pointlist.map(({ x, y }) => [x, y])
|
||
} else {
|
||
series.data = []
|
||
}
|
||
},
|
||
|
||
// 重绘控制点
|
||
redrawCtrlPointBySeriesName() {
|
||
const series = findSeriesByName(this.option.series, 'BaseLine_Ctrl_Point')
|
||
const data = this.isEnergy() ? this.shapeEnergyData : this.shapeChannelData
|
||
series.data = data.map(({ size, color, point: { x, y } }) => {
|
||
return {
|
||
value: [x, y],
|
||
itemStyle: {
|
||
color: 'transparent',
|
||
borderColor: color,
|
||
borderWidth: size / 2,
|
||
},
|
||
}
|
||
})
|
||
},
|
||
|
||
// 重绘Peak Line
|
||
redrawPeakLine() {
|
||
this.option.series = this.option.series.filter((item) => {
|
||
return !item.name.includes('Peak_')
|
||
})
|
||
|
||
const data = this.isEnergy() ? this.energyPeakGroup : this.channelPeakGroup
|
||
const peakLines = []
|
||
data.forEach((item, index) => {
|
||
peakLines.push(
|
||
buildLineSeries(
|
||
`Peak_${index}`,
|
||
item.pointlist.map(({ x, y }) => [x, y]),
|
||
item.color,
|
||
{
|
||
zlevel: 6,
|
||
}
|
||
)
|
||
)
|
||
})
|
||
|
||
this.option.series.push(...peakLines)
|
||
},
|
||
|
||
// 重绘右上角的缩略图
|
||
redrawThumbnailChart() {
|
||
const series = this.thumbnailOption.series[0]
|
||
const data = this.isEnergy() ? this.shadowEnergyChart : this.shadowChannelChart
|
||
series.data = data.pointlist.map(({ x, y }) => [x, y])
|
||
},
|
||
|
||
// 点击图表,设置红线
|
||
handleChartClick(param) {
|
||
const { offsetX, offsetY } = param
|
||
const point = getXAxisAndYAxisByPosition(this.$refs.chartRef.getChartInstance(), offsetX, offsetY)
|
||
if (point) {
|
||
const xAxis = point[0]
|
||
this.option.series[0].markLine.data[0].xAxis = xAxis
|
||
|
||
const channel = this.isEnergy() ? this.getChannelByEnergy(xAxis) : parseInt(xAxis.toFixed())
|
||
const energy = this.isEnergy()
|
||
? xAxis.toFixed(2)
|
||
: this.allEnergy.pointlist && this.allEnergy.pointlist[channel - 1].x.toFixed(2)
|
||
const counts = this.isEnergy() ? this.allEnergy.pointlist[channel - 1] : this.allChannel.pointlist[channel - 1]
|
||
this.option.title.text = `{a|Channel:${channel}} {a|Energy:${energy}} {a|Counts:${counts.y}} {a|Detectability:0}`
|
||
|
||
this.getSelPosNuclide(channel)
|
||
}
|
||
},
|
||
|
||
// 获取 Nuclide Library 数据
|
||
async getSelPosNuclide(channel) {
|
||
try {
|
||
this.loading = true
|
||
const { sampleId, inputFileName: fileName } = this.sample
|
||
|
||
const { success, result, message } = await getAction('/gamma/getSelPosNuclide', {
|
||
sampleId,
|
||
channel,
|
||
fileName,
|
||
})
|
||
if (success) {
|
||
const { identify } = result
|
||
this.nuclideLibraryList = identify
|
||
} else {
|
||
this.$message.error(message)
|
||
}
|
||
} catch (error) {
|
||
this.list = []
|
||
console.error(error)
|
||
} finally {
|
||
this.loading = false
|
||
}
|
||
},
|
||
|
||
resize() {
|
||
this.$refs.chartRef.resize()
|
||
this.$refs.thumbnailChartRef.resize()
|
||
this.closePeakInfomationTooltip()
|
||
},
|
||
|
||
// peak info 点击左右方向
|
||
handlePeakInfoChange(direction) {
|
||
this.moveMarkLine(direction)
|
||
},
|
||
|
||
// 触发Peak Infomation
|
||
handleTogglePeak() {
|
||
const xAxis = this.option.series[0].markLine.data[0].xAxis
|
||
const channel = this.isEnergy() ? this.getChannelByEnergy(xAxis) : parseInt(xAxis.toFixed())
|
||
const index = this.channelPeakGroup.findIndex((peakItem) => {
|
||
const allX = peakItem.pointlist.map((item) => item.x)
|
||
const max = Math.max(...allX)
|
||
const min = Math.min(...allX)
|
||
return channel >= min && channel <= max
|
||
})
|
||
if (-1 !== index) {
|
||
this.getPeakInfo(index)
|
||
} else {
|
||
this.closePeakInfomationTooltip()
|
||
}
|
||
},
|
||
|
||
// 获取 Peak 峰顶 信息
|
||
async getPeakInfo(index) {
|
||
try {
|
||
const { inputFileName: fileName } = this.sample
|
||
const { success, result, message } = await getAction('/gamma/clickPeakInformation', {
|
||
fileName,
|
||
index,
|
||
})
|
||
if (success) {
|
||
const html = result.replaceAll('\n', '<br>')
|
||
const currPeak = this.channelPeakGroup[index]
|
||
const { x, y } = currPeak.pointlist.reduce((prev, curr) => {
|
||
return prev && prev.y > curr.y ? prev : curr
|
||
})
|
||
|
||
const chart = this.$refs.chartRef.getChartInstance()
|
||
|
||
const [xPix, yPix] = chart.convertToPixel({ seriesIndex: 0 }, [x, y])
|
||
this.peakInfomationTooltip.content = html
|
||
this.peakInfomationTooltip.visible = true
|
||
this.peakInfomationTooltip.left = xPix
|
||
this.peakInfomationTooltip.top = yPix - 20
|
||
} else {
|
||
this.$message.error(message)
|
||
}
|
||
} catch (error) {
|
||
console.error(error)
|
||
}
|
||
},
|
||
|
||
// 关闭Peak Infomation
|
||
closePeakInfomationTooltip() {
|
||
this.peakInfomationTooltip.visible = false
|
||
},
|
||
|
||
/**
|
||
* 向某一个方向移动标记线
|
||
* @param { 'left'| 'right' } direction
|
||
*/
|
||
moveMarkLine(direction) {
|
||
const prevAxis = this.option.series[0].markLine.data[0].xAxis
|
||
|
||
// 获取每一段 Channel 中的最大值
|
||
const maxXAxises = this.channelPeakGroup.map((item) => {
|
||
const allY = item.pointlist.map((item) => item.y)
|
||
const max = item.pointlist.find((point) => point.y == Math.max(...allY))
|
||
return max.x
|
||
})
|
||
if (direction == 'right') {
|
||
// 找到第一个比prevAxis大的xAxis
|
||
const find = maxXAxises.find((xAxis) => xAxis > prevAxis)
|
||
if (find) {
|
||
this.option.series[0].markLine.data[0].xAxis = find
|
||
}
|
||
} else if (direction == 'left') {
|
||
// 找到第一个比prevAxis小的xAxis
|
||
const find = maxXAxises.reverse().find((xAxis) => xAxis < prevAxis)
|
||
if (find) {
|
||
this.option.series[0].markLine.data[0].xAxis = find
|
||
}
|
||
}
|
||
},
|
||
|
||
// 鼠标按下时开启可刷选状态
|
||
handleMouseDown() {
|
||
const chart = this.$refs.chartRef.getChartInstance()
|
||
chart.dispatchAction({
|
||
type: 'takeGlobalCursor',
|
||
// 如果想变为“可刷选状态”,必须设置。不设置则会关闭“可刷选状态”。
|
||
key: 'brush',
|
||
brushOption: {
|
||
// 参见 brush 组件的 brushType。如果设置为 false 则关闭“可刷选状态”。
|
||
brushType: 'rect',
|
||
},
|
||
})
|
||
},
|
||
|
||
handleMouseUp() {
|
||
setTimeout(() => {
|
||
const chart = this.$refs.chartRef.getChartInstance()
|
||
this.clearBrush(chart)
|
||
}, 0)
|
||
},
|
||
|
||
clearBrush(chart) {
|
||
// 清理刷选的范围
|
||
chart.dispatchAction({
|
||
type: 'brush',
|
||
areas: [],
|
||
})
|
||
|
||
// 改为不可刷选状态
|
||
chart.dispatchAction({
|
||
type: 'takeGlobalCursor',
|
||
})
|
||
},
|
||
|
||
// 刷选完毕时
|
||
handleBrushEnd(param) {
|
||
this.closePeakInfomationTooltip()
|
||
|
||
const chart = this.$refs.chartRef.getChartInstance()
|
||
const areas = param.areas[0]
|
||
if (areas) {
|
||
const range = areas.range
|
||
const [[minX, maxX], [minY, maxY]] = range
|
||
|
||
const point1 = chart.convertFromPixel({ seriesIndex: 0 }, [minX, minY]).map((num) => parseInt(num.toFixed()))
|
||
const point2 = chart.convertFromPixel({ seriesIndex: 0 }, [maxX, maxY]).map((num) => parseInt(num.toFixed()))
|
||
|
||
const xAxisMax = chart.getModel().getComponent('xAxis').axis.scale._extent[1]
|
||
const yAxisMax = this.option.yAxis.max
|
||
|
||
let [x1, y2, x2, y1] = [...point1, ...point2] // 根据解析出的数据确定真实的范围
|
||
|
||
const xAxisLimit = rangeNumber(1, xAxisMax)
|
||
const yAxisLimit = rangeNumber(1, yAxisMax)
|
||
|
||
x1 = xAxisLimit(x1)
|
||
x2 = xAxisLimit(x2)
|
||
|
||
y1 = yAxisLimit(y1)
|
||
y2 = yAxisLimit(y2)
|
||
|
||
this.option.xAxis.min = x1
|
||
this.option.xAxis.max = x2
|
||
this.option.yAxis.min = y1
|
||
this.option.yAxis.max = y2
|
||
|
||
if (this.isEnergy()) {
|
||
const channel1 = this.getChannelByEnergy(x1)
|
||
const channel2 = this.getChannelByEnergy(x2)
|
||
this.setThumbnailChartRect(channel1, y2, channel2, y1)
|
||
} else {
|
||
this.setThumbnailChartRect(x1, y2, x2, y1)
|
||
}
|
||
|
||
const thumbnailChart = this.$refs.thumbnailChartRef.getChartInstance()
|
||
const [, maxYPixel] = thumbnailChart.convertToPixel({ seriesIndex: 0 }, [0, y1]) // 方框的上下两条边的yAxis转为pix
|
||
const [, minYPixel] = thumbnailChart.convertToPixel({ seriesIndex: 0 }, [0, y2])
|
||
const rectHeightPixel = maxYPixel - minYPixel // 计算方框的左右边长(pix)
|
||
this.halfHeightPixel = rectHeightPixel / 2
|
||
}
|
||
|
||
this.clearBrush(chart)
|
||
},
|
||
|
||
// 在右上角缩略图中设置范围
|
||
setThumbnailChartRect(x1, y2, x2, y1) {
|
||
this.thumbnailChartRect = [x1, y2, x2, y1]
|
||
|
||
const { markLine } = this.thumbnailOption.series[0]
|
||
const pointList = [
|
||
[
|
||
[x1, y1],
|
||
[x2, y1],
|
||
],
|
||
[
|
||
[x2, y1],
|
||
[x2, y2],
|
||
],
|
||
[
|
||
[x2, y2],
|
||
[x1, y2],
|
||
],
|
||
[
|
||
[x1, y2],
|
||
[x1, y1],
|
||
],
|
||
]
|
||
const lines = pointList.map((point) => {
|
||
return this.generateLineDataByTwoPoints(point[0], point[1])
|
||
})
|
||
markLine.data = lines
|
||
},
|
||
|
||
// 缩略图点击
|
||
handleThumbnailChartClick(param) {
|
||
const { offsetX, offsetY } = param
|
||
const thumbnailChart = this.$refs.thumbnailChartRef.getChartInstance()
|
||
|
||
const point = getXAxisAndYAxisByPosition(thumbnailChart, offsetX, offsetY)
|
||
if (point && this.thumbnailChartRect && this.thumbnailChartRect.length) {
|
||
const [x1, y2, x2, y1] = this.thumbnailChartRect
|
||
const halfWidth = Math.ceil((x2 - x1) / 2)
|
||
|
||
const [, maxYAxisPixel] = thumbnailChart.convertToPixel({ seriesIndex: 0 }, [0, this.thumbnailOption.yAxis.max]) // 缩略图最大值转为pix
|
||
const [, minYAxisPixel] = thumbnailChart.convertToPixel({ seriesIndex: 0 }, [0, 1])
|
||
|
||
let [xAxis, yAxis] = point
|
||
|
||
const xAxisMax = thumbnailChart.getModel().getComponent('xAxis').axis.scale._extent[1]
|
||
|
||
const xAxisLimit = rangeNumber(1 + halfWidth, xAxisMax - halfWidth)
|
||
|
||
const halfHeightPixel = this.halfHeightPixel
|
||
const yAxisLimit = rangeNumber(maxYAxisPixel + halfHeightPixel, minYAxisPixel - halfHeightPixel)
|
||
|
||
xAxis = xAxisLimit(xAxis)
|
||
|
||
let [, yAxisPixel] = thumbnailChart.convertToPixel({ seriesIndex: 0 }, [0, yAxis])
|
||
yAxisPixel = yAxisLimit(yAxisPixel)
|
||
|
||
const minYAxis = thumbnailChart.convertFromPixel({ seriesIndex: 0 }, [0, yAxisPixel + halfHeightPixel])[1] // 再把y轴最小值从pix转为yAxis
|
||
const maxYAxis = thumbnailChart.convertFromPixel({ seriesIndex: 0 }, [0, yAxisPixel - halfHeightPixel])[1]
|
||
|
||
this.setThumbnailChartRect(xAxis - halfWidth, maxYAxis, xAxis + halfWidth, minYAxis)
|
||
|
||
if (this.isEnergy()) {
|
||
const x1 = parseInt(this.shadowEnergyChart.pointlist[xAxis - halfWidth].x)
|
||
const x2 = parseInt(this.shadowEnergyChart.pointlist[xAxis + halfWidth].x)
|
||
|
||
this.option.xAxis.min = x1
|
||
this.option.xAxis.max = x2
|
||
} else {
|
||
this.option.xAxis.min = xAxis - halfWidth
|
||
this.option.xAxis.max = xAxis + halfWidth
|
||
}
|
||
|
||
this.option.yAxis.min = minYAxis
|
||
this.option.yAxis.max = maxYAxis
|
||
}
|
||
},
|
||
|
||
// 重置
|
||
handleReset() {
|
||
this.option.xAxis.min = 1
|
||
this.option.xAxis.max = 'dataMax'
|
||
this.option.yAxis.min = 1
|
||
this.option.yAxis.max = Math.ceil(Math.max(...this.shadowChannelChart.pointlist.map((item) => item.y)) * 1.1)
|
||
|
||
this.thumbnailOption.series[0].markLine.data = []
|
||
this.thumbnailChartRect = []
|
||
this.closePeakInfomationTooltip()
|
||
},
|
||
|
||
// 从分析工具刷新部分数据
|
||
handleRefresh(data) {
|
||
const { allData, shadowChannelChart, shadowEnergyChart, shapeChannelData, shapeEnergyData } = data
|
||
|
||
const channelPeakGroup = allData.filter((item) => item.name == 'Peak' && item.group == 'channel')
|
||
const energyPeakGroup = allData.filter((item) => item.name == 'Peak' && item.group == 'energy')
|
||
|
||
const channelBaseLine = allData.find((item) => item.name == 'BaseLine' && item.group == 'channel')
|
||
const energyBaseLine = allData.find((item) => item.name == 'BaseLine' && item.group == 'energy')
|
||
|
||
const channelLcLine = allData.find((item) => item.name == 'Lc' && item.group == 'channel')
|
||
const energyLcLine = allData.find((item) => item.name == 'Lc' && item.group == 'energy')
|
||
|
||
const channelScacLine = allData.find((item) => item.name == 'Scac' && item.group == 'channel')
|
||
const energyScacLine = allData.find((item) => item.name == 'Scac' && item.group == 'energy')
|
||
|
||
this.allEnergy = allData.find((item) => item.name == 'Energy' && item.group == 'energy')
|
||
this.allChannel = allData.find((item) => item.name == 'Count' && item.group == 'channel')
|
||
|
||
// 保存Peak Line
|
||
this.channelPeakGroup = channelPeakGroup
|
||
this.energyPeakGroup = energyPeakGroup
|
||
|
||
// 保存 Spectrum Line
|
||
this.shadowChannelChart = shadowChannelChart
|
||
this.shadowEnergyChart = shadowEnergyChart
|
||
|
||
// 保存基线
|
||
this.channelBaseLine = channelBaseLine
|
||
this.energyBaseLine = energyBaseLine
|
||
|
||
// 保存Lc
|
||
this.channelLcLine = channelLcLine
|
||
this.energyLcLine = energyLcLine
|
||
|
||
// 保存Scac
|
||
this.channelScacLine = channelScacLine
|
||
this.energyScacLine = energyScacLine
|
||
|
||
// 保存 基线控制点
|
||
this.shapeChannelData = shapeChannelData
|
||
this.shapeEnergyData = shapeEnergyData
|
||
|
||
this.opts.notMerge = true
|
||
this.redrawPeakLine()
|
||
this.redrawCtrlPointBySeriesName()
|
||
|
||
this.redrawLineBySeriesName('BaseLine', this.energyBaseLine, this.channelBaseLine, this.graphAssistance.Baseline)
|
||
this.redrawLineBySeriesName('LcLine', this.energyLcLine, this.channelLcLine, this.graphAssistance.Lc)
|
||
this.redrawLineBySeriesName('ScacLine', this.energyScacLine, this.channelScacLine, this.graphAssistance.SCAC)
|
||
this.redrawLineBySeriesName('Spectrum', this.shadowEnergyChart, this.shadowChannelChart)
|
||
|
||
this.redrawThumbnailChart()
|
||
this.$nextTick(() => {
|
||
this.resetChartOpts()
|
||
})
|
||
},
|
||
|
||
// 分析工具Accept时刷新部分数据
|
||
handleAccept() {
|
||
console.log('%c [ 分析工具Accept时刷新部分数据 ]-1046', 'font-size:13px; background:pink; color:#bf2c9f;')
|
||
},
|
||
|
||
// 重置图表配置
|
||
resetChartOpts() {
|
||
this.opts.notMerge = false
|
||
this.option.brush = { toolbox: [] }
|
||
},
|
||
|
||
/**
|
||
* 根据两个点生成一个markLine直线
|
||
*/
|
||
generateLineDataByTwoPoints(point1, point2) {
|
||
return [
|
||
{
|
||
xAxis: point1[0],
|
||
yAxis: point1[1],
|
||
},
|
||
{
|
||
xAxis: point2[0],
|
||
yAxis: point2[1],
|
||
},
|
||
]
|
||
},
|
||
|
||
getChannelByEnergy(energy) {
|
||
let channel = 0
|
||
for (let index = 1; index < this.allEnergy.pointlist.length; index++) {
|
||
const currEnergy = this.allEnergy.pointlist[index].x
|
||
if (currEnergy >= energy) {
|
||
const prevEnergy = this.allEnergy.pointlist[index - 1].x
|
||
if (currEnergy - energy > energy - prevEnergy.x) {
|
||
channel = index
|
||
} else {
|
||
channel = index + 1
|
||
}
|
||
break
|
||
}
|
||
}
|
||
return channel
|
||
},
|
||
|
||
// 重置页面信息
|
||
reset() {
|
||
this.selectedChannel = -1
|
||
this.nuclideLibraryList = []
|
||
this.closePeakInfomationTooltip()
|
||
this.option.series = []
|
||
this.thumbnailOption.series = []
|
||
this.option.xAxis.name = 'Channel'
|
||
this.option.yAxis.type = 'value'
|
||
|
||
if (this.option.series.length) {
|
||
this.option.series[0].type = 'line'
|
||
this.option.series[0].symbol = 'none'
|
||
this.option.series[0].markLine.lineStyle.width = 2
|
||
}
|
||
|
||
if (this.thumbnailOption.series.length) {
|
||
this.thumbnailOption.series[0].type = 'line'
|
||
this.thumbnailOption.series[0].symbol = 'none'
|
||
}
|
||
this.graphAssistance = cloneDeep(graphAssistance)
|
||
},
|
||
|
||
handleColorChange(colorConfig) {
|
||
// 如果还没加载完,加载新的
|
||
if (this.isLoading) {
|
||
this.getSampleDetail()
|
||
}
|
||
// 否则修改已有颜色
|
||
else {
|
||
this.changeColor(colorConfig)
|
||
}
|
||
},
|
||
|
||
// 修改颜色
|
||
changeColor(colorConfig) {
|
||
const { Color_Spec, Color_Peak, Color_Lc, Color_Base, Color_Scac, Color_Compare, Color_Strip, Color_Fitbase } =
|
||
colorConfig
|
||
|
||
this.shadowChannelChart.color = Color_Spec
|
||
this.shadowEnergyChart.color = Color_Spec
|
||
|
||
for (let i = 0; i < this.channelPeakGroup.length; i++) {
|
||
this.channelPeakGroup[i].color = Color_Peak
|
||
this.energyPeakGroup[i].color = Color_Peak
|
||
}
|
||
|
||
this.channelLcLine.color = Color_Lc
|
||
this.energyLcLine.color = Color_Lc
|
||
|
||
this.channelBaseLine.color = Color_Base
|
||
this.energyBaseLine.color = Color_Base
|
||
|
||
this.channelScacLine.color = Color_Scac
|
||
this.energyScacLine.color = Color_Scac
|
||
|
||
this.changeColorBySeriesName('Spectrum', Color_Spec)
|
||
this.changePeakLineColor(Color_Peak)
|
||
this.changeColorBySeriesName('LcLine', Color_Lc)
|
||
this.changeColorBySeriesName('BaseLine', Color_Base)
|
||
this.changeColorBySeriesName('ScacLine', Color_Scac)
|
||
|
||
const thumbnailChartSeries = findSeriesByName(this.thumbnailOption.series, 'Spectrum')
|
||
thumbnailChartSeries.itemStyle.color = Color_Spec
|
||
},
|
||
|
||
// 根据series名修改颜色
|
||
changeColorBySeriesName(seriesName, color) {
|
||
const series = findSeriesByName(this.option.series, seriesName)
|
||
series.itemStyle.color = color
|
||
},
|
||
|
||
// 改变Peak的颜色
|
||
changePeakLineColor(color) {
|
||
this.option.series
|
||
.filter((item) => item.name.includes('Peak_'))
|
||
.forEach((item) => {
|
||
item.itemStyle.color = color
|
||
})
|
||
},
|
||
|
||
isEnergy() {
|
||
return this.graphAssistance.axisType == 'Energy'
|
||
},
|
||
},
|
||
watch: {
|
||
sample: {
|
||
handler(newVal, oldVal) {
|
||
console.log('newValnewVal', newVal)
|
||
if (newVal.sampleId) {
|
||
this.getSampleDetail()
|
||
} else {
|
||
this.getSampleDetail_file()
|
||
}
|
||
},
|
||
immediate: true,
|
||
},
|
||
},
|
||
}
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.gamma-analysis {
|
||
height: 100%;
|
||
|
||
.ant-spin-nested-loading {
|
||
height: 100%;
|
||
|
||
::v-deep {
|
||
.ant-spin-container {
|
||
height: 100%;
|
||
}
|
||
}
|
||
}
|
||
|
||
&-main {
|
||
height: calc(100% - 51px);
|
||
display: flex;
|
||
overflow: auto hidden;
|
||
position: relative;
|
||
}
|
||
|
||
&-chart {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
&-thumbnail {
|
||
position: absolute;
|
||
top: 50px;
|
||
right: 10px;
|
||
width: 500px;
|
||
height: 20%;
|
||
background-color: #153e44;
|
||
}
|
||
}
|
||
</style>
|
||
<style lang="less">
|
||
.peak-infomation-tooltip {
|
||
position: absolute;
|
||
border-style: solid;
|
||
white-space: nowrap;
|
||
z-index: 9;
|
||
box-shadow: rgba(0, 0, 0, 0.2) 1px 2px 10px;
|
||
border-width: 1px;
|
||
border-radius: 4px;
|
||
padding: 10px;
|
||
border-color: rgb(255, 255, 255);
|
||
pointer-events: none;
|
||
background-color: #55a9fe;
|
||
border-color: #55a9fe;
|
||
}
|
||
</style>
|