From 4e394d1f4ed0928d5498083621966aba390a7642 Mon Sep 17 00:00:00 2001 From: wujingjing <gersonwu@qq.com> Date: 星期三, 05 三月 2025 16:05:15 +0800 Subject: [PATCH] 图表查询 --- src/components/chat/chatComponents/summaryCom/components/recordSet/RecordSet.vue | 398 +++++++++++++++++++++++++++++++++++++++++++------------- 1 files changed, 301 insertions(+), 97 deletions(-) diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSet/RecordSet.vue b/src/components/chat/chatComponents/summaryCom/components/recordSet/RecordSet.vue index 2c32c23..2f082a3 100644 --- a/src/components/chat/chatComponents/summaryCom/components/recordSet/RecordSet.vue +++ b/src/components/chat/chatComponents/summaryCom/components/recordSet/RecordSet.vue @@ -1,12 +1,12 @@ <!-- 鏄ㄦ棩渚涙按绠$綉姒傚喌 --> <template> - <div class="w-full"> - <div class="flex mb-4 flex-wrap"> + <div class="w-full flex-column"> + <div class="flex mb-4 flex-wrap flex-0"> <!-- TimeRange v-model 璺� @change 涓殑鍊间細涓嶄竴鏍凤紝浠change 涓负鍑� --> - <template v-if="visibleParams && visibleParams.length > 0"> + <template v-if="visibleParams && visibleParams.length > 0 && showFilter"> <component - class="flex-0 m-1" - v-model="paramsValueList[index].value" + class="flex-0 m-2" + v-model="visibleParams[index].value" v-for="(item, index) in visibleParams as any" :key="item.id" :id="item.id" @@ -14,19 +14,40 @@ :data="item" :originData="originData" @change="(val) => handleQueryChange(val, item)" - :disabled="chartLoading" + :disabled="chartLoading || disabled" ></component> </template> <slot> </slot> + <YRange @input="yRangeInput" /> + <el-tooltip + v-if="originChartType === ChartTypeEnum.Score" + :content="`${Object.keys(scoreMap) + .map((key) => `${key} 琛ㄧず${scoreMap[key]}`) + .join(', ')}`" + placement="top-start" + > + <SvgIcon name="fa fa-question-circle-o" :size="15" class="ml-1 cursor-help flex-center" color="#909399" /> + </el-tooltip> - <YRange v-model="yRange" @input="yRangeInput" /> - <el-checkbox class="m-1" v-model="isMultiCompare" label="澶氭棩瀵规瘮" @change="multiCompareChange"></el-checkbox> + <el-checkbox class="m-2" v-model="isMultiCompare" label="澶氭棩瀵规瘮" @change="multiCompareChange"></el-checkbox> <DisplayMode class="ml-auto" v-model="showMode" @change="displayModeChange" /> </div> - <RecordSetTable :data="tableData" v-if="tableIsShow" :key="tableKey" /> - <div v-show="!tableIsShow" :style="{ height: chartHeight }" v-resize="chartContainerResize" v-loading="chartLoading"> + <RecordSetTable + :data="tableData" + v-if="tableIsShow" + :key="tableKey" + :tableLimitHeight="tableLimitHeight" + :class="{ 'flex-auto': chartHeight == undefined }" + /> + <div + v-show="!tableIsShow" + :style="{ height: chartHeight }" + :class="{ 'flex-auto': chartHeight == undefined }" + v-resize="chartContainerResize" + v-loading="chartLoading" + > <div ref="chartRef"></div> </div> </div> @@ -34,7 +55,6 @@ <script setup lang="ts"> import type * as echarts from 'echarts'; -import _ from 'lodash'; import moment from 'moment'; import type { PropType } from 'vue'; import { computed, ref, shallowRef, watch } from 'vue'; @@ -44,6 +64,7 @@ import RecordSetTable from '../recordSetTable/RecordSetTable.vue'; import DisplayMode from './components/DisplayMode.vue'; import YRange from './components/YRange.vue'; +import { IS_DAY_LIST } from './components/constants'; import { DisplayModeType } from './components/types'; import type { RecordSetParamsItem } from './types'; import { RecordSetParamsType, recordSetMapCom, scoreMap } from './types'; @@ -51,11 +72,8 @@ import { axisLabelFormatter } from '/@/utils/chart'; import { deepClone } from '/@/utils/other'; import { debounce } from '/@/utils/util'; +import { defaultsDeep, groupBy, random } from 'lodash-es'; const chartRef = ref<HTMLDivElement>(null); -const yRange = ref({ - min: null as number, - max: null as number, -}); const showMode = ref(DisplayModeType.Chart); // const props = defineProps({ @@ -63,6 +81,10 @@ // type: Object as PropType<RecordSet>, // }, // }); + +const emits = defineEmits<{ + (event: 'updateQuery', res: any): void; +}>(); const props = defineProps({ data: { @@ -76,11 +98,31 @@ }, chartHeight: { type: String, - default: '20rem', + required: false, }, -}) as { - data: any; -}; + tableHeight: { + type: Number, + default: document.body.clientHeight * 0.7, + }, + showFilter: { + type: Boolean, + default: true, + }, + disabled: { + type: Boolean, + default: false, + }, + reportIndex: { + type: Number, + default: 0, + }, + historyId: { + type: String, + }, +}); + +const tableLimitHeight = props.chartHeight == undefined ? undefined : props.tableHeight; + const chartLoading = ref(false); const stepOptions = [ @@ -91,13 +133,16 @@ { title: '1澶�', value: '1 days' }, ]; -const visibleParams = computed(() => { +const getVisibleParams = (data) => { // const visibleList = props.data?.params?.filter((item) => !item?.hide) ?? []; // index 浣滀负 id - const visibleList = (props.data?.filter ?? []).map((item, index) => ({ - id: index + '', - ...item, - })); + const dataFilter = data?.filter ?? []; + const visibleList = (data?.filter ?? []).map((item, index) => { + // 涓嶄慨鏀瑰師濮嬪湴鍧� + item.id = index + ''; + + return item; + }); const newList: RecordSetParamsItem[] = []; for (let index = 0; index < visibleList.length; index++) { const current = visibleList[index]; @@ -116,7 +161,7 @@ id: current.id, type: RecordSetParamsType.Step, origin: current, - value: current.value, + value: current.step_value, list: stepOptions, title: current.title, }); @@ -128,8 +173,35 @@ } return newList; -}); -const paramsValueList = ref(deepClone(visibleParams.value)); +}; + +// 鏇存敼 value 鍊硷紝瀹屽叏瑕嗙洊浼氫涪澶卞搷搴旀�э紝鍙慨鏀� value +const updateVisibleParams = (data) => { + const filter = data?.filter ?? []; + + filter.map((newItem, index) => { + const currentItem = visibleParams.value.find((item) => item.id === index + ''); + + if (currentItem) { + if (newItem.type === RecordSetParamsType.TimeRange) { + currentItem.value = [newItem.start_value, newItem.end_value]; + } else { + currentItem.value = newItem.step_value; + } + } + }); +}; +const visibleParams = ref(getVisibleParams(props.data)); + +const checkIsDayTime = () => { + if (!props.showFilter) return false; + const stepFilter = visibleParams.value.find((item) => item.type === RecordSetParamsType.Step); + if (!stepFilter.origin.step_value) return false; + + return IS_DAY_LIST.includes(stepFilter.origin.step_value); +}; +// 璺ㄥ害鏄惁鏄棩鏈熷舰寮� +// const isDayTime = checkIsDayTime(); let groupedValues = null; let timeIndex = undefined; let valueIndex = undefined; @@ -141,7 +213,7 @@ let preData = null; let activeChartType: ChartTypeEnum = props.data?.chart_type ?? ChartTypeEnum.Line; -const originChartType = activeChartType; +let originChartType = activeChartType; // 缁欒〃鏍肩敤鐨� series const currentSeries = shallowRef<any[]>(null); @@ -195,7 +267,11 @@ const values = groupedValues[item]; return { name: item === 'default' ? '' : item, - data: values.map((item) => [item[timeIndex], item[valueIndex]]), + data: values + .map((item) => [item[timeIndex], item[valueIndex]]) + .toSorted((b, a) => { + return b[timeIndex].localeCompare(a[timeIndex]); + }), ...getChartTypeSeriesOption(activeChartType), }; }); @@ -208,12 +284,10 @@ } : axisLabelFormatter; - const tooltipValueFormatter = - originChartType === ChartTypeEnum.Score - ? (value) => { - return scoreMap[value]; - } - : undefined; + const tooltipValueFormatter = (value) => { + const realValue = originChartType === ChartTypeEnum.Score ? scoreMap[value] : value; + return realValue + (props.data.unit ? ` ${props.data.unit}` : ''); + }; const scoreYAxisOption: echarts.YAXisComponentOption = { min: 0, @@ -223,7 +297,7 @@ formatter: yAxisFormatter, }, }; - const combineOption = _.defaultsDeep( + const combineOption = defaultsDeep( { grid: { bottom: 20, @@ -284,7 +358,8 @@ name: timeCol?.title, }, yAxis: { - name: valueCol?.title, + name: props.data.unit ? `${props.data.unit}` : valueCol?.title, + /** @description 涓嶅己鍒朵繚鐣� */ scale: true, ...(originChartType === ChartTypeEnum.Score ? scoreYAxisOption : {}), @@ -299,8 +374,7 @@ }); }; -const handleData = () => { - const data = props.data; +const handleData = (data = props.data) => { if (!data || !data.cols || !data.values) { return; } @@ -326,7 +400,7 @@ nameIndex = 1; } nameCol = data.cols[nameIndex]; - groupedValues = _.groupBy(data.values, (item) => item[nameIndex]); + groupedValues = groupBy(data.values, (item) => item[nameIndex]); } else if (data.chart === 'single_line') { groupedValues = { default: data.values, @@ -338,42 +412,118 @@ nameIndex = 1; } nameCol = data.cols[nameIndex]; - groupedValues = _.groupBy(data.values, (item) => item[nameIndex]); + groupedValues = groupBy(data.values, (item) => item[nameIndex]); } }; -const drawChart = () => { - const data = props.data; +const drawChart = (data = props.data) => { if (!data || !data.cols || !data.values) { return; } + handleData(); setNewOption(); }; -const { chartContainerResize, chartInstance, initChart } = useDrawChatChart({ chartRef, drawChart }); +const { chartContainerResize, chartInstance } = useDrawChatChart({ chartRef, drawChart }); -// 鏇存崲鍒楄〃 -const changeMap = new Map<string, any>(null); +const updateCurrent = (res, isNew = false) => { + const title = res?.title; + const values = res?.values ?? []; + //#region ====================== 鍒锋柊褰撳墠 filter ====================== + // 鍙洿鏂� value锛屼笉鐩存帴瑕嗙洊锛岄槻姝涪澶卞搷搴旀�� + // updateVisibleParams(res); + //#endregion + groupedValues = groupBy(values, (item) => item[nameIndex]); + if (isMultiCompare.value) { + handleMultiCompare(); + } else { + if (isNew) { + setNewOption(); + } else { + (currentSeries.value = + groupedValues && + Object.keys(groupedValues).map((item, index) => { + const values = groupedValues[item]; + return { + name: item === 'default' ? '' : item, + data: values + .map((item) => [item[timeIndex], item[valueIndex]]) + .toSorted((b, a) => { + return b[timeIndex].localeCompare(a[timeIndex]); + }), + }; + })), + chartInstance.value?.setOption({ + title: { + text: title, + }, + series: currentSeries.value, + }); + } + } +}; + +const getFilterList = () => { + const curAgentKey = props.data.agent_key; + + // 鐩稿悓 agent_key 涓嬫墍鏈� filter 璇锋眰鍙傛暟 + const filterList = ((props as any).originData?.content?.origin?.summary ?? []).reduce((preVal, curVal) => { + if (curVal.agent_key !== curAgentKey) return preVal; + + const filter = (curVal.filter ?? []).reduce((subPreVal, subCurVal) => { + if (subCurVal.type === RecordSetParamsType.TimeRange) { + subPreVal.push( + ...[ + { + update: subCurVal.update, + value: subCurVal.start_value, + path: subCurVal.start_path, + }, + { + update: subCurVal.update, + value: subCurVal.end_value, + path: subCurVal.end_path, + }, + ] + ); + } else { + subPreVal.push({ + update: subCurVal.update, + value: subCurVal.step_value, + path: subCurVal.step_path, + }); + } + + return subPreVal; + }, []); + + preVal = preVal.concat(filter); + + return preVal; + }, []); + return filterList; +}; const handleQueryChange = async (val: any, item: RecordSetParamsItem) => { if (!val) return; - const historyId = (props as any).originData.historyId; + const historyId = props.historyId; let res = null; - try { - if (item.type === RecordSetParamsType.TimeRange) { - item.origin.start_value = val[0]; - item.origin.end_value = val[1]; - changeMap.set(item.id, item.origin); - } else { - item.origin.value = val; - changeMap.set(item.id, item.origin); - } + // 鏀瑰彉鍘熷鍊� + if (item.type === RecordSetParamsType.TimeRange) { + item.origin.start_value = val[0]; + item.origin.end_value = val[1]; + } else { + item.origin.step_value = val; + } - const filterObj = Array.from(changeMap.values()); + const filterList = getFilterList(); + try { const params = { history_id: historyId, + // 鏌ヨ鍓嶅悗 agent_key 涓嶄細鍙� agent_key: props.data.agent_key, - filter_json: JSON.stringify(filterObj), + filter_json: JSON.stringify(filterList), + result_group_index: props.reportIndex, }; res = await curveQuery(params); chartLoading.value = true; @@ -381,28 +531,14 @@ chartLoading.value = false; } - const title = res?.values?.title; - const values = res?.values?.values ?? []; - groupedValues = _.groupBy(values, (item) => item[nameIndex]); + emits('updateQuery', res); - if (isMultiCompare.value) { - handleMultiCompare(); - } else { - (currentSeries.value = - groupedValues && - Object.keys(groupedValues).map((item) => { - const values = groupedValues[item]; - return { - data: values.map((item) => [item[timeIndex], item[valueIndex]]), - }; - })), - chartInstance.value.setOption({ - title: { - text: title, - }, - series: currentSeries.value, - }); - } + return; +}; + +let realRange = { + min: null, + max: null, }; const getSingleDayOption = (day = COMMON_DAY) => @@ -454,11 +590,9 @@ } as echarts.EChartsOption); //#region ====================== 璁剧疆Y鑼冨洿 ====================== const debounceSetYRange = debounce((val) => { + (realRange.min = val.min), (realRange.max = val.max); chartInstance.value.setOption({ - yAxis: { - min: val.min, - max: val.max, - }, + yAxis: realRange, }); currentSeries.value = currentSeries.value.concat([]); @@ -481,7 +615,7 @@ const seriesData = Object.keys(cloneData).reduce((preVal, curVal, curIndex, arr) => { const values = cloneData[curVal]; const isMulti = arr.length > 1; - const groupByDateValues = _.groupBy(values, (item) => moment(item[timeIndex]).format('YYYY-MM-DD')); + const groupByDateValues = groupBy(values, (item) => moment(item[timeIndex]).format('YYYY-MM-DD')); for (const key in groupByDateValues) { if (Object.prototype.hasOwnProperty.call(groupByDateValues, key)) { const val = groupByDateValues[key]; @@ -500,7 +634,11 @@ }, []); const series = seriesData.map<echarts.SeriesOption>((item) => ({ name: item[0]?.[nameIndex], - data: item.map((item) => [item[timeIndex], item[valueIndex]]), + data: item + .map((item) => [item[timeIndex], item[valueIndex]]) + .toSorted((b, a) => { + return b[timeIndex].localeCompare(a[timeIndex]); + }), ...getChartTypeSeriesOption(activeChartType), })); setNewOption(series, getSingleDayOption()); @@ -527,17 +665,25 @@ const tableData = computed(() => { if (!currentSeries.value) return []; - // const min = yRange.value.min == null ? -Infinity : yRange.value.min; - // const max = yRange.value.max == null ? Infinity : yRange.value.max; + const min = realRange.min == null ? -Infinity : realRange.min; + const max = realRange.max == null ? Infinity : realRange.max; const timeDataMap = currentSeries.value.reduce((preVal, curVal, index) => { for (const item of curVal.data) { - const [time, value] = item; - - // if (value < min || value > max) { - // continue; - // } + let [time, value] = item; + // 澶氭棩瀵规瘮锛屽彧鏄剧ず鏃跺垎绉� + if (isMultiCompare.value) { + time = time.slice(11); + } + if (value < min || value > max) { + continue; + } if (!preVal[time]) { preVal[time] = []; + } + + // score 绫诲瀷锛寁alue 鍊硷紝闇�瑕佹槧灏勬垚鍙︿竴涓�� + if (originChartType === ChartTypeEnum.Score) { + value = scoreMap[value]; } preVal[time][index] = value; } @@ -545,11 +691,23 @@ return preVal; }, {}); - const data = Object.keys(timeDataMap).map((item) => [item, ...timeDataMap[item]]); + // 鏃堕棿鏈�鏃╁埌鏈�鏅氭帓搴� + const data = Object.keys(timeDataMap) + .map((item) => [item, ...timeDataMap[item]]) + .toSorted((b, a) => { + return b[0].localeCompare(a[0]); + }); + const getColName = (name) => { + if (props.data.unit) { + return `${name}锛�${props.data.unit}锛塦; + } + return name; + }; const cols = currentSeries.value.map((item, index) => ({ - title: item.name ?? `鍊�${index + 1}`, + title: getColName(item.name ?? `鍊�${index + 1}`), type: 'text', })); + cols.unshift({ title: '鏃堕棿', type: 'time', @@ -562,7 +720,6 @@ cols: cols, values: data, }; - return result; }); //#endregion @@ -571,16 +728,63 @@ watch( () => currentSeries.value, (val) => { - if (!tableIsShow.value) return; - tableKey.value = _.random(0, 100000) + ''; + tableKey.value = random(0, 100000) + ''; } ); + +const updateAll = (triggerIndex, res) => { + // 褰撳墠 agent_key + const curAgentKey = props.data.agent_key; + const triggerAgentKey = (props as any).originData?.content?.origin?.summary?.[triggerIndex]?.agent_key; + if (curAgentKey !== triggerAgentKey) { + return; + } + if (!curAgentKey) { + return; + } + + // 褰撳墠椤规墍鍦ㄧ储寮� + let currentIndex = -1; + for (let index = 0; index < (props as any).originData.content.origin.summary.length; index++) { + const item = (props as any).originData.content.origin.summary[index]; + if (item.agent_key === curAgentKey) { + currentIndex++; + if (index === props.summaryIndex) { + break; + } + } + } + + const newSummary = res?.summary?.[currentIndex]; + if (!newSummary) return; + + updateCurrent(newSummary); +}; + +const updateIndexSummary = (summary) => { + updateCurrent(summary?.[props.summaryIndex], true); +}; + +const clearChart = () => { + chartInstance.value.setOption( + { + title: { + text: '', + }, + series: [], + }, + true + ); +}; defineExpose({ drawChart, isMultiCompare, handleMultiCompare, handleData, + updateAll, + clearChart, + updateIndexSummary, }); </script> <style scoped lang="scss"></style> -- Gitblit v1.9.3