wujingjing
2025-03-05 4e394d1f4ed0928d5498083621966aba390a7642
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 类型,value 值,需要映射成另一个值
         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>