| | |
| | | <template> |
| | | <div class="flex flex-col h100"> |
| | | <el-row class="w100 h100"> |
| | | <el-col :span="17"> |
| | | <el-col :span="17" class="h-full"> |
| | | <div class="h100 relative"> |
| | | <div class="ml-6 h100"> |
| | | <el-tabs v-model="state.activeMetricName" @tab-change="handleClick" class="h100"> |
| | | <el-tab-pane label="基础信息" name="basicInformation"> |
| | | <div class="h100"> |
| | | <el-descriptions title="指标信息" :column="2"> |
| | | <el-descriptions-item label="指标名称:">{{ |
| | | state.descriptionQuotaItems.title ? state.descriptionQuotaItems.title : '-' |
| | | <el-descriptions title="指标信息"> |
| | | <el-descriptions-item label="指标名称:" :span="2">{{ |
| | | state.metricBasicInfo.descriptionQuotaItems.title ? state.metricBasicInfo.descriptionQuotaItems.title : '-' |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="指标全称:">{{ |
| | | state.descriptionQuotaItems.full_name ? state.descriptionQuotaItems.full_name : '-' |
| | | <el-descriptions-item label="指标全称:" :span="2">{{ |
| | | state.metricBasicInfo.descriptionQuotaItems.full_name |
| | | ? state.metricBasicInfo.descriptionQuotaItems.full_name |
| | | : '-' |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="指标定义:">{{ |
| | | state.descriptionQuotaItems.metrics_define ? state.descriptionQuotaItems.metrics_define : '-' |
| | | <el-descriptions-item label="指标定义:" :span="3">{{ |
| | | state.metricBasicInfo.descriptionQuotaItems.metrics_define |
| | | ? state.metricBasicInfo.descriptionQuotaItems.metrics_define |
| | | : '-' |
| | | }}</el-descriptions-item> |
| | | <el-descriptions-item label="类别:"> |
| | | <el-descriptions-item label="类别:" :span="2"> |
| | | {{ |
| | | state.descriptionQuotaItems.metrics_type ? state.descriptionQuotaItems.metrics_type : '-' |
| | | state.metricBasicInfo.descriptionQuotaItems.metrics_type |
| | | ? state.metricBasicInfo.descriptionQuotaItems.metrics_type |
| | | : '-' |
| | | }}</el-descriptions-item |
| | | > |
| | | <el-descriptions-item label="重要性:"> |
| | | {{ eMetrics_Ops[state.descriptionQuotaItems.metrics_important] }} |
| | | <el-descriptions-item label="重要性:" :span="2"> |
| | | {{ eMetrics_Ops[state.metricBasicInfo.descriptionQuotaItems.metrics_important] }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="计算方法:"> |
| | | {{ state.descriptionQuotaItems.calcu_method ? state.descriptionQuotaItems.calcu_method : '-' }} |
| | | <el-descriptions-item label="计算方法:" :span="3"> |
| | | {{ |
| | | state.metricBasicInfo.descriptionQuotaItems.calcu_method |
| | | ? state.metricBasicInfo.descriptionQuotaItems.calcu_method |
| | | : '-' |
| | | }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | <el-divider /> |
| | | </div> |
| | | </el-tab-pane> |
| | | <el-tab-pane label="指标探索" name="indicatorExploration"> |
| | | <div class="h100"> |
| | | <div class="set_explore"> |
| | | <div class="set_explore_pad"> |
| | | <el-form ref="exploreFormRef" :model="state.exploreForm" label-width="80px" label-position="left"> |
| | | <el-form-item label="日期区间:"> |
| | | <div style="width: 300px"> |
| | | <el-date-picker |
| | | v-model="state.exploreForm.dateRangeExplore" |
| | | type="daterange" |
| | | range-separator="~" |
| | | value-format="YYYY-MM-DD HH:mm:ss" |
| | | :default-time="defaultTime" |
| | | @change="getDateTime" |
| | | :disabled-date="disablesDate" |
| | | <el-tab-pane label="指标探索" name="indicatorExploration" class="h-full"> |
| | | <div class="h-full flex-column"> |
| | | <el-form :model="dialogFormValue" class="flex-0" ref="dialogFormRef" :rules="dialogFormRules"> |
| | | <el-form-item label="日期区间:" prop="rangValue" v-if="currentMetrics?.is_time_values ?? true"> |
| | | <TimeRange v-model="dialogFormValue.rangValue" @change="query(false)" /> |
| | | </el-form-item> |
| | | <el-row :gutter="35"> |
| | | <el-col :span="12" class="mb20"> |
| | | <el-form-item label="分组维度:" prop="rangValue"> |
| | | <el-select v-model="dialogFormValue.groupDimList" multiple @change="query(false)"> |
| | | <el-option |
| | | v-for="item in currentMetrics?.dimensions ?? []" |
| | | :key="item.id" |
| | | :label="item.title" |
| | | :value="item.id" |
| | | /> |
| | | </div> |
| | | </el-form-item> |
| | | <el-form-item label="维度下钻:"> |
| | | <el-select v-model="state.exploreForm.dimensionDrilling" autocomplete="off" style="width: 226.4px" clearable> |
| | | <el-option |
| | | v-for="item in Object.keys(eDrilling_Ops)" |
| | | :key="item" |
| | | :value="parseInt(item)" |
| | | :label="eDrilling_Ops[item]" |
| | | > |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="维度筛选:"> |
| | | <el-select |
| | | v-model="state.exploreForm.dimensionFiltering.user" |
| | | autocomplete="off" |
| | | style="width: 186.4px" |
| | | clearable |
| | | @change="handleDimensionFilteringChange" |
| | | > |
| | | <el-option |
| | | v-for="item in Object.keys(eDrilling_Ops)" |
| | | :key="item" |
| | | :value="parseInt(item)" |
| | | :label="eDrilling_Ops[item]" |
| | | > |
| | | </el-option> |
| | | </el-select> |
| | | <el-select |
| | | v-model="state.exploreForm.dimensionFiltering.unit" |
| | | autocomplete="off" |
| | | style="width: 126.4px; margin: 0 5px" |
| | | clearable |
| | | > |
| | | <el-option |
| | | v-for="item in Object.keys(eDimensionFilter_Ops)" |
| | | :key="item" |
| | | :value="parseInt(item)" |
| | | :label="eDimensionFilter_Ops[item]" |
| | | > |
| | | </el-option> |
| | | </el-select> |
| | | <el-select v-model="state.resultList" autocomplete="off" style="width: 126.4px; margin-right: 5px" clearable> |
| | | <el-option |
| | | v-for="(item, index) in state.resultList" |
| | | :key="index" |
| | | :value="item.department" |
| | | :label="item.department" |
| | | > |
| | | </el-option> |
| | | </el-select> |
| | | <el-button type="primary" @click="handleExplore" icon="ele-Search">查询</el-button> |
| | | </el-col> |
| | | <el-col :span="12" class="mb20" v-for="item in filterDimList" :key="item.id" |
| | | ><el-form-item :label="`${item.title}:`" :prop="`${item.id}`"> |
| | | <el-input v-model="dialogFormValue[item.id]" @input="(val) => filterDimInput(val, item)"> </el-input> |
| | | </el-form-item> |
| | | </el-form> |
| | | </div> |
| | | <div class="w100 h-[550px]"> |
| | | <div ref="chartExplorationRef" class="w100 h100"></div> |
| | | </div> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | <div class="flex-auto mt-4"> |
| | | <SummaryCom |
| | | v-loading="queryLoading" |
| | | ref="summaryComRef" |
| | | :data="querySummaryData" |
| | | :origin-data="{ |
| | | content: { |
| | | origin: { |
| | | summary: querySummaryData, |
| | | }, |
| | | }, |
| | | }" |
| | | /> |
| | | <!-- <RecordSet v-bind="recordSetProps" class="h-full" /> --> |
| | | <!-- <RecordSetTable v-bind="recordSetTableProps" class="h-full"/> --> |
| | | </div> |
| | | </div> |
| | | </el-tab-pane> |
| | | <el-tab-pane label="指标图谱" name="metricGraph" class="h-full"> |
| | | <div class="h-full"> |
| | | <TreeGraph v-if="graphData" :data="graphData" class="h-full" :maxCount="maxCount" /></div |
| | | ></el-tab-pane> |
| | | </el-tabs> |
| | | </div> |
| | | <div class="list_btn" @click="handleExitFlow"> |
| | |
| | | <div class="set-detail"> |
| | | <el-divider /> |
| | | <div class="section___vePzi"> |
| | | <div class="item___txXyB" v-for="(item, index) in state.metricBasicInfo.visitNumData" :key="index"> |
| | | <div class="item_name">{{ item.name }}:</div> |
| | | <div class="item___txXyB"> |
| | | <div class="item_name">重要性:</div> |
| | | <div class="item_value">{{ eMetrics_Ops[state.metricBasicInfo.descriptionQuotaItems.metrics_important] }}</div> |
| | | </div> |
| | | <div class="item___txXyB"> |
| | | <div class="item_name">所属领域:</div> |
| | | <div class="item_value"> |
| | | {{ item.value }} |
| | | {{ |
| | | state.metricBasicInfo.descriptionQuotaItems.metrics_type |
| | | ? state.metricBasicInfo.descriptionQuotaItems.metrics_type |
| | | : '-' |
| | | }} |
| | | </div> |
| | | </div> |
| | | <div class="item___txXyB"> |
| | | <div class="item_name">描述:</div> |
| | | <div class="item_value"> |
| | | {{ |
| | | state.metricBasicInfo.descriptionQuotaItems.metrics_define |
| | | ? state.metricBasicInfo.descriptionQuotaItems.metrics_define |
| | | : '-' |
| | | }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | <i class="ywifont ywicon-gongzuozongjie w-[18px] h-[18px] !text-[18px]"></i> |
| | | <span>创建信息</span> |
| | | </div> |
| | | <div class="item___txXyB" v-for="(item, index) in state.metricBasicInfo.createInformation" :key="index"> |
| | | <div class="item_name">{{ item.name }}:</div> |
| | | <div class="item___txXyB"> |
| | | <div class="item_name">创建人:</div> |
| | | <div class="item_value"> |
| | | {{ item.value }} |
| | | {{ |
| | | state.metricBasicInfo.descriptionQuotaItems.create_user |
| | | ? state.metricBasicInfo.descriptionQuotaItems.create_user |
| | | : '-' |
| | | }} |
| | | </div> |
| | | </div> |
| | | <div class="item___txXyB"> |
| | | <div class="item_name">创建时间:</div> |
| | | <div class="item_value"> |
| | | {{ |
| | | state.metricBasicInfo.descriptionQuotaItems.create_time |
| | | ? state.metricBasicInfo.descriptionQuotaItems.create_time |
| | | : '-' |
| | | }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | <div class="subTitle___zya5g">下钻维度</div> |
| | | <div class="ml-0 mt-[20px]"> |
| | | <div class="flex flex-wrap gap-[5px] items-center"> |
| | | <div v-for="item in state.metricBasicInfo.applicationInformation" :key="item.id"> |
| | | <div v-for="item in currentMetrics?.dimensions ?? []" :key="item.id"> |
| | | <el-tag :style="{ backgroundColor: 'e6f4ff', color: '#0958d9', borderColor: '#91caff' }">{{ |
| | | item.title |
| | | }}</el-tag> |
| | |
| | | import { nextTick, onMounted, reactive, ref } from 'vue'; |
| | | import { useRoute, useRouter } from 'vue-router'; |
| | | import * as metricApi from '/@/api/metrics'; |
| | | import TimeRange from '/@/components/chat/chatComponents/summaryCom/components/recordSet/components/TimeRange.vue'; |
| | | import { formatTime, getDefaultPeriod } from '/@/utils/istation/common.js'; |
| | | import { eMetrics_Ops } from '/@/views/types/metrics'; |
| | | import { eDimensionFilter_Ops, eDrilling_Ops } from '/@/views/types/metrics/index'; |
| | | import TreeGraph from '/@/components/graph/treeGraph/TreeGraph.vue'; |
| | | |
| | | import { FormRules } from 'element-plus/es/components/form/src/types'; |
| | | import _, { debounce } from 'lodash'; |
| | | import { computed } from 'vue'; |
| | | import SummaryCom from './components/SummaryCom.vue'; |
| | | import { chatMetricsJsonByPost } from '/@/api/metrics'; |
| | | import { useCompRef } from '/@/utils/types'; |
| | | import { OrgTreeItem } from '../agentGraph/types'; |
| | | const defaultTime = ref<[Date, Date]>([new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)]); |
| | | const router = useRouter(); |
| | | const route = useRoute(); |
| | | // 定义变量内容 |
| | | |
| | | const state = reactive({ |
| | | metricInfo: { |
| | | name: '', |
| | | id: '', |
| | | } as any, |
| | | activeMetricName: 'basicInformation', |
| | | descriptionQuotaItems: {} as any, |
| | | metricBasicInfo: { |
| | | visitNumData: [ |
| | | { |
| | | name: '重要性', |
| | | value: '', |
| | | }, |
| | | { |
| | | name: '所属模型', |
| | | value: '', |
| | | }, |
| | | { |
| | | name: '描述', |
| | | value: '', |
| | | }, |
| | | ], |
| | | createInformation: [ |
| | | { |
| | | name: '创建人', |
| | | value: 'admin', |
| | | }, |
| | | { |
| | | name: '创建时间', |
| | | value: '2024-10-08 10:03:39', |
| | | }, |
| | | { |
| | | name: '更新时间', |
| | | value: '2024-10-08 14:12:11', |
| | | }, |
| | | ], |
| | | applicationInformation: [], |
| | | }, |
| | | descriptionQuotaItems: {}, |
| | | } as any, |
| | | exploreForm: { |
| | | dateRangeExplore: '' as any, |
| | | dimensionDrilling: 0, |
| | |
| | | }, |
| | | resultList: [ |
| | | { |
| | | id: 1, |
| | | department: 'HR', |
| | | }, |
| | | { |
| | | id: 2, |
| | | department: 'marketing', |
| | | }, |
| | | { |
| | | id: 3, |
| | | department: 'sales', |
| | | }, |
| | | { |
| | | id: 4, |
| | | department: 'strategy', |
| | | }, |
| | | ], |
| | |
| | | |
| | | //#endregion |
| | | //#region ====================== 基础信息 ====================== |
| | | const currentMetrics = ref(null); |
| | | const tableData = ref([]); |
| | | const getTableData = async (id) => { |
| | | const res = await metricApi.getMetricNameListByPost(); |
| | | const data = res?.values ?? []; |
| | | const filterData = data.filter((item) => item.id == id); |
| | | state.descriptionQuotaItems = filterData[0]; |
| | | state.metricBasicInfo.visitNumData = [ |
| | | { |
| | | name: '重要性', |
| | | value: eMetrics_Ops[filterData[0].metrics_important], |
| | | }, |
| | | { |
| | | name: '所属模型', |
| | | value: '-', |
| | | }, |
| | | { |
| | | name: '描述', |
| | | value: filterData[0].metrics_define, |
| | | }, |
| | | ]; |
| | | state.metricBasicInfo.applicationInformation = filterData[0].dimensions ?? []; |
| | | state.metricBasicInfo.descriptionQuotaItems = filterData[0]; //左侧描述内容 |
| | | currentMetrics.value = filterData[0]; |
| | | filterDimList.value.forEach((item) => { |
| | | if (!dialogFormValue.value) { |
| | | dialogFormValue.value = {} as any; |
| | | } |
| | | dialogFormValue.value[item.id] = ''; |
| | | }); |
| | | }; |
| | | //#endregion |
| | | //#region ====================== 指标探索查询 ====================== |
| | | //#region ====================== 指标图谱查询 ====================== |
| | | // 时间限制 |
| | | const disablesDate = (time) => { |
| | | return time.getTime() > new Date().getTime(); |
| | |
| | | }); |
| | | } |
| | | state.activeMetricName = val; |
| | | if (val === 'metricGraph') { |
| | | if (!graphData.value) { |
| | | getGraphTreeData(); |
| | | } |
| | | } |
| | | }; |
| | | //#endregion |
| | | |
| | | //#region ====================== 指标图谱 ====================== |
| | | |
| | | const filterDimList = computed( |
| | | () => currentMetrics?.value?.dimensions ?? [].filter((item) => item.filter_type === 'str_eq' && item.type === '字符串') |
| | | ); |
| | | const dialogFormRules = ref<FormRules>({ |
| | | title: [{ required: true, message: '请输入标题', trigger: 'blur' }], |
| | | prompt: [{ required: true, message: '请输入提示词', trigger: 'blur' }], |
| | | }); |
| | | |
| | | const summaryComRef = useCompRef(SummaryCom); |
| | | const dialogFormValue = ref({ |
| | | rangValue: ['2024-10-21 00:00:00', '2024-10-23 23:59:59'], |
| | | groupDimList: [], |
| | | }); |
| | | |
| | | const currentMetricsId = computed(() => router.currentRoute.value.query.id as string); |
| | | const querySummaryData = ref([]); |
| | | const queryLoading = ref(false); |
| | | /** |
| | | * 按查询条件查找 |
| | | */ |
| | | const query = async (isFirst: boolean) => { |
| | | if (!currentMetricsId.value) return; |
| | | const groupDimStr = dialogFormValue.value.groupDimList.join(','); |
| | | const sendGroupDims = groupDimStr || undefined; |
| | | queryLoading.value = true; |
| | | const res = await chatMetricsJsonByPost( |
| | | { |
| | | metrics_id: currentMetricsId.value, |
| | | start_time: dialogFormValue.value.rangValue[0], |
| | | end_time: dialogFormValue.value.rangValue[1], |
| | | group_dims: sendGroupDims, |
| | | filter_dims: JSON.stringify(_.omit(dialogFormValue.value, ['groupDimList', 'rangValue'])), |
| | | }, |
| | | { |
| | | loading: false, |
| | | } |
| | | ).finally(() => { |
| | | queryLoading.value = false; |
| | | }); |
| | | |
| | | querySummaryData.value = res?.summary ?? []; |
| | | if (!isFirst) { |
| | | nextTick(() => { |
| | | nextTick(() => { |
| | | summaryComRef.value.updateSummary(); |
| | | }); |
| | | }); |
| | | } |
| | | }; |
| | | const debounceQuery = debounce(query, 600); |
| | | const filterDimInput = (val, dim) => { |
| | | debounceQuery(false); |
| | | }; |
| | | |
| | | //#endregion |
| | | |
| | | //#region ====================== 指标图谱 ====================== |
| | | |
| | | const maxCount = ref(null); |
| | | const graphData = ref(null); |
| | | const convertOrgTreeToTreeNode = (orgTreeData: OrgTreeItem) => { |
| | | const treeData = { |
| | | id: orgTreeData.treeId, |
| | | label: orgTreeData.label, |
| | | data: orgTreeData, |
| | | children: orgTreeData.children?.length > 0 ? orgTreeData.children.map((item) => convertOrgTreeToTreeNode(item)) : [], |
| | | }; |
| | | return treeData; |
| | | }; |
| | | const getGraphTreeData = () => { |
| | | if (!currentMetricsId.value) return; |
| | | /** @description 维度数量 */ |
| | | let dimensionCount = 0; |
| | | /** @description 指标数量 */ |
| | | let metricsCount = 1; |
| | | |
| | | const metricsTreeId = `metrics-${currentMetricsId.value}`; |
| | | const dimensionList = currentMetrics.value.dimensions ?? []; |
| | | dimensionCount = dimensionList.length; |
| | | let logicTree: OrgTreeItem = { |
| | | treeId: metricsTreeId, |
| | | logicId: currentMetricsId.value, |
| | | type: 'metrics', |
| | | |
| | | model: currentMetrics.value, |
| | | get label() { |
| | | return this.model.title; |
| | | }, |
| | | level: 0, |
| | | children: dimensionList.map((item) => { |
| | | const dimensionTreeId = `${metricsTreeId}-dimension-${item.id}`; |
| | | return { |
| | | treeId: dimensionTreeId, |
| | | logicId: item.id, |
| | | type: 'dimension', |
| | | model: item, |
| | | get label() { |
| | | return this.model.title; |
| | | }, |
| | | level: 1, |
| | | }; |
| | | }), |
| | | }; |
| | | const resData = logicTree; |
| | | maxCount.value = Math.max(dimensionCount, metricsCount); |
| | | graphData.value = convertOrgTreeToTreeNode(resData); |
| | | }; |
| | | //#endregion |
| | | |
| | | onMounted(() => { |
| | | const { id } = route.query; |
| | | query(true); |
| | | getTableData(id); |
| | | }); |
| | | </script> |