From a32fc7bdf0ae1fccecfee1228e7348b8f2c478a6 Mon Sep 17 00:00:00 2001 From: wujingjing <gersonwu@qq.com> Date: 星期三, 27 十一月 2024 10:59:07 +0800 Subject: [PATCH] 地图优化 --- src/components/chat/chatComponents/summaryCom/components/recordSetTable/RecordSetTable.vue | 783 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 660 insertions(+), 123 deletions(-) diff --git a/src/components/chat/chatComponents/summaryCom/components/recordSetTable/RecordSetTable.vue b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/RecordSetTable.vue index 17d4558..1a02bcc 100644 --- a/src/components/chat/chatComponents/summaryCom/components/recordSetTable/RecordSetTable.vue +++ b/src/components/chat/chatComponents/summaryCom/components/recordSetTable/RecordSetTable.vue @@ -1,86 +1,122 @@ <!-- 鏌ヨ鏈�鏂拌鍛婁俊鎭� --> <template> <div class="w-full flex-column"> - <div class="flex-0 flex-items-center mb-1 h-[38px]"> - <template v-if="visibleParams && visibleParams.length > 0"> + <div class="flex-0 flex-items-center mb-1 flex-wrap"> + <template v-if="visibleParams && visibleParams.length > 0 && showFilter"> <component class="flex-0 m-2" + :class="{ invisible: showMode2 === DisplayModeType2.Map }" v-model="visibleParams[index].value" v-for="(item, index) in visibleParams as any" :key="item.id" :id="item.id" - :is="recordSetTableMap[item.type]" + :is="recordSetMapCom[item.type]" :data="item" @change="(val) => handleQueryChange(val, item)" :originData="originData" + :disabled="disabled" ></component> </template> - <ColFilter class="ml-auto" :columnList="colList" @change="colFilterChange" /> + <DisplayMode + v-if="isTotalTable" + class="ml-auto" + :order="modeChangeOrder" + v-model="showMode" + :displayModeTypeMap="displayModeTypeMap" + @change="displayModeChange" + /> + <div class="ml-auto space-x-2 flex-items-center"> + <ColFilter v-if="!isTotalTable" :columnList="colList" @change="colFilterChange" /> + + <DisplayMode + v-if="isMap" + :order="modeChangeOrder2" + :displayModeTypeMap="displayModeTypeMap2" + v-model="showMode2" + @change="displayModeChange2" + /> + </div> </div> - <div class="flex-auto" ref="containerRef" v-resize="resizeHandler" v-loading="queryLoading"> - <el-table - ref="tableRef" - :maxHeight="tableHeight" - :cell-style="tableCellStyle" - :header-cell-style="tableHeaderCellStyle" - :data="chunkTableData[pager.index - 1]" - @row-click="recordSelectChange" - rowClassName="cursor-pointer" - :spanMethod="objectSpanMethod" - class="w-full h-full" - highlightCurrentRow - cellClassName="text-sm" - headerCellClassName="text-sm" - > - <DefineColumns v-slot="{ cols }"> - <template v-for="item in colList" :key="item.prop"> - <el-table-column - v-if="item.isShow ?? true" - :type="item.type" - :label="item.label" - :width="item.width" - :sortable="item.sortable" - :key="item.prop" - :prop="item.prop" - show-overflow-tooltip - /> - </template> - </DefineColumns> - <el-table-column v-if="data?.title" :label="data?.title" show-overflow-tooltip> - <template v-if="data?.cols?.length > 0"> + <div + class="flex-auto flex-col" + style="display: flex" + v-show="(isTotalTable && showMode === DisplayModeType.List) || (isMap && showMode2 === DisplayModeType2.List)" + > + <div class="flex-auto" ref="containerRef" v-resize="resizeHandler" v-loading="queryLoading"> + <el-table + ref="tableRef" + :maxHeight="tableHeight" + :cell-style="tableCellStyle" + :header-cell-style="tableHeaderCellStyle" + :data="tableData" + @row-click="recordSelectChange" + @sort-change="sortChange" + :spanMethod="objectSpanMethod" + class="w-full h-full" + highlightCurrentRow + :rowClassName="tableRowClassName" + cellClassName="text-sm" + headerCellClassName="text-sm" + > + <DefineColumns v-slot="{ cols }"> <template v-for="item in colList" :key="item.prop"> <el-table-column v-if="item.isShow ?? true" :type="item.type" :label="item.label" :width="item.width" - :sortable="item.sortable" + :sortable="item.sortable ? 'custom' : false" :key="item.prop" :prop="item.prop" + @sortChange="sortChange" show-overflow-tooltip /> </template> + </DefineColumns> + <el-table-column v-if="data?.title" :label="data?.title" show-overflow-tooltip> + <template v-if="tableCols.length > 0"> + <template v-for="item in colList" :key="item.prop"> + <el-table-column + v-if="item.isShow ?? true" + :type="item.type" + :label="item.label" + :width="item.width" + :sortable="item.sortable ? 'custom' : false" + :key="item.prop" + :prop="item.prop" + show-overflow-tooltip + /> + </template> + </template> + </el-table-column> + <template v-else> + <template v-if="tableCols.length > 0"> + <ReuseColumns :cols="colList" /> + </template> </template> - </el-table-column> - <template v-else> - <template v-if="data?.cols?.length > 0"> - <ReuseColumns :cols="colList" /> - </template> - </template> - </el-table> + </el-table> + </div> + + <el-pagination + class="flex-0" + style="margin-bottom: 0 !important; margin-left: auto !important; float: none !important" + small + hide-on-single-page + v-model:current-page="pager.index" + v-model:page-size="pager.size" + layout="total,prev,pager,next,jumper" + :total="pager.total" + /> </div> - <el-pagination - class="flex-0" - style="margin-bottom: 0 !important; margin-left: auto !important; float: none !important" - small - hide-on-single-page - v-model:current-page="pager.index" - v-model:page-size="pager.size" - layout="total,prev,pager,next,jumper" - :total="pager.total" - /> + <div class="flex-auto" v-if="showMode2 === DisplayModeType2.Map"> + <MapView :data="data" /> + </div> + + <div class="flex-auto" v-resize="debounceResizeChart" v-show="showMode === DisplayModeType.Chart"> + <div ref="chartRef" style="height: 25rem"></div> + </div> <InfoDetail class="text-base" v-model="infoDetailIsShow" :item="detailMapRow" :colList="colList" /> </div> @@ -88,18 +124,25 @@ <script setup lang="ts"> import { createReusableTemplate } from '@vueuse/core'; +import * as echarts from 'echarts'; import type { TableInstance } from 'element-plus'; -import _ from 'lodash'; import type { CSSProperties } from 'vue'; -import { computed, nextTick, onMounted, reactive, ref, type PropType, watchEffect } from 'vue'; +import { computed, nextTick, onMounted, reactive, ref, shallowRef, watchEffect, type PropType } from 'vue'; +import { PATH_ICON } from '../../../common'; +import { ChartTypeEnum } from '../../../types'; import { BORDER_COLOR, COL_HEADER_CELL_BG_COLOR, THICK_BORDER_WIDTH } from '../deviceLastValue/constants'; +import DisplayMode from '../recordSet/components/DisplayMode.vue'; +import { DisplayModeType, DisplayModeType2, displayModeTypeMap2, displayModeTypeMap } from '../recordSet/components/types'; +import { RecordSetParamsType, recordSetMapCom } from '../recordSet/types'; +import MapView from './map/Map.vue'; +import InfoDetail from './infoDetail/InfoDetail.vue'; +import { curveQuery } from '/@/api/ai/chat'; import ColFilter from '/@/components/table/colFilter/ColFilter.vue'; import { TableCol } from '/@/components/table/colFilter/types'; -import { debounce, getTextWidth } from '/@/utils/util'; -import InfoDetail from './infoDetail/InfoDetail.vue'; -import { RecordSetTableType, recordSetTableMap } from './types'; -import StringInput from './components/StringInput.vue'; -import { curveQuery } from '/@/api/ai/chat'; +import { axisLabelFormatter } from '/@/utils/chart'; +import { LocalPlus } from '/@/utils/storage'; +import { debounce, getTextWidth, toPercent } from '/@/utils/util'; +import { sortBy, chunk } from 'lodash-es'; const props = defineProps({ data: { @@ -111,10 +154,18 @@ summaryIndex: { type: Number, }, - tableLimitHeight:{ - type:Number, - required:false - } + tableLimitHeight: { + type: Number, + required: false, + }, + showFilter: { + type: Boolean, + default: true, + }, + disabled: { + type: Boolean, + default: false, + }, }); const [DefineColumns, ReuseColumns] = createReusableTemplate<{ @@ -124,10 +175,110 @@ (event: 'updateQuery', res: any): void; }>(); +const checkIsTotalTable = (propsData) => { + return propsData?.hasOwnProperty('agg_count_col') ?? false; +}; + +const isMap = computed(() => { + return props.data?.hasOwnProperty('map') ?? false; +}); + +const isTotalTable = computed(() => checkIsTotalTable(props.data)); + +const getTableCols = (propsData) => { + const current = propsData?.cols ?? []; + + if (checkIsTotalTable(propsData)) { + current.push({ + title: '姣斾緥', + type: 'text', + }); + } + return current; +}; + +// 缁熻琛ㄦ牸锛岄渶瑕佸鍔犳瘮渚嬪垪 +const tableCols = ref(getTableCols(props.data)); + +// 鍊煎垪绱㈠紩 +let valueColIndex; +// 姣斾緥鍒楃储寮� +let rateColIndex; +// 鎬绘暟 +let sumValue; +const getTableValues = (propsData) => { + const agg_count_col = propsData?.agg_count_col; + const current = (propsData?.values ?? []) as Array<any[]>; + if (checkIsTotalTable(propsData)) { + const last = current[current.length - 1]; + const lastItemIndex = last?.length > 0 ? last.length - 1 : 0; + valueColIndex = agg_count_col ?? lastItemIndex; + + rateColIndex = valueColIndex + 1; + // 璁$畻鎬绘暟 + sumValue = current.reduce((pre, cur) => { + const value = Number(cur[valueColIndex] ?? 0); + pre += value; + return pre; + }, 0); + // 璁$畻姣斾緥锛屽苟鏀惧埌姣斾緥琛� + current.forEach((rowValue) => { + const value = rowValue[valueColIndex]; + const rate = sumValue === 0 ? 0 : (value ?? 0) / sumValue; + const percent = toPercent(rate, true, 2); + + rowValue.splice(rateColIndex, 0, percent); + }); + // 娣诲姞鍚堣琛� + if (last?.length > 0) { + const totalRow = new Array(last.length); + + let lastGroupIndex = tableCols.value.findLastIndex((item, index) => index !== rateColIndex && index !== valueColIndex); + lastGroupIndex = lastGroupIndex === -1 ? 0 : lastGroupIndex; + totalRow[lastGroupIndex] = '鍚堣'; + // 姣斾緥鍒� + totalRow[rateColIndex] = '100%'; + // 鍚堣鎬绘暟 + totalRow[valueColIndex] = sumValue + ''; + (totalRow as any).isTotal = true; + current.push(totalRow); + } + } + return current; +}; + +const tableRowClassName = ({ row, rowIndex }) => { + let className = 'cursor-pointer'; + if (row.isTotal) { + const bgColor = '!bg-[#c5d9f1]'; + className += ` font-bold ${bgColor} hover:${bgColor} active:${bgColor}`; + } + return className; +}; +// 缁熻琛ㄦ牸锛岄渶瑕佸鍔犵疮璁″�艰锛屼互鍙婃瘮渚嬪垪 +const tableValues = ref(getTableValues(props.data)); +const storeCols = (colList: any[]) => { + const key = colList.map((item) => item.label).join(','); + if (!key) return; + LocalPlus.set(key, colList, 7); +}; const colList = ref([]); +const getStoreCols = (colList: any[]) => { + if (colList.length === 0) return colList; + const key = colList.map((item) => item.label).join(','); + if (!key) return colList; + const storeValue = LocalPlus.get(key); + + if (!storeValue) { + return colList; + } else { + return storeValue; + } +}; + watchEffect(() => { - colList.value = - props.data.cols?.map((item, index) => { + const originData = + tableCols.value?.map((item, index) => { let isShow = true; if (props.data.max_cols != null) { isShow = index < props.data.max_cols; @@ -136,11 +287,14 @@ ...item, width: 0, label: item.title, - // sortable: item.type === 'time', + sortable: !!item.name, prop: index + '', isShow: isShow, } as TableCol; }) ?? []; + + const storeCols = getStoreCols(originData); + colList.value = storeCols; }); // 鎵�鏈夋樉绀虹殑 prop @@ -153,7 +307,7 @@ const filterGroupIndexList = computed(() => { const groupIndexList = []; - (props.data?.cols ?? []).map((item, index) => { + tableCols.value.map((item, index) => { // 鏄垎缁勶紝涓斿綋鍓嶅彲瑙� if (item.group && visibleColProp.value.includes(index + '')) { groupIndexList.push(index); @@ -163,7 +317,7 @@ return groupIndexList; }); -const chunkTableData = ref([props.data?.values]); +const chunkTableData = ref([tableValues.value]); // FIXME: 濡傛灉鍏ㄩ儴 group 閮戒负 true锛屾伆濂芥渶鍚庡鏉¤褰曚篃鍙互褰掍负涓�缁勶紝寰堝彲鑳芥樉绀轰笉瀵癸紝灏戜簡鍑犳潯閲嶅鐨勬暟鎹� const objectSpanMethod = ({ row, column, rowIndex, columnIndex }) => { const key = `${rowIndex},${columnIndex}`; @@ -197,14 +351,11 @@ return values; } - const groupData = _.groupBy(values, (item, index) => { - const groupValue = item[curGroupIndex]; - return groupValue; - }); + const groupData = getItemMap(values, curGroupIndex, true); // 椤哄欢涓嬩竴涓垎缁� i++; // 閲嶆柊鎺掑竷涓�涓嬩綅缃紝淇濊瘉 group 鐩搁偦锛屽悓鏃舵墦涓� rowspan - const result = Object.values(groupData).reduce((preVal, curVal) => { + const result = Object.values(Array.from(groupData.values())).reduce((preVal, curVal) => { curVal.map((item, index) => { // 琛屽垪浣滀负 key const key = `${j + index},${curGroupIndex}`; @@ -218,6 +369,28 @@ return result; }; +const getItemMap = <T>(arr: T[], defaultProps = 'ID', isMultiple = false) => { + if (!arr || arr.length === 0) return {}; + + const result = arr.reduce((acc, curr) => { + if (isMultiple) { + if (!acc.get(curr[defaultProps])) { + acc.set(curr[defaultProps], [curr]); + } else { + { + /* acc[curr[defaultProps]].push(curr); */ + } + acc.get(curr[defaultProps]).push(curr); + } + } else { + acc.set(curr[defaultProps], curr); + } + return acc; + }, new Map()) as Map<string, T | T[]>; + + return result; +}; + /** * 璁$畻姣忎竴鍒楃殑瀹藉害 * 1锛夋�诲灏忎簬瀹瑰櫒瀹斤細鐩存帴鎸夋瘮渚嬪垎 @@ -227,7 +400,7 @@ */ const calcColWidth = (width) => { // 杩斿洖鎵�鏈夋爣棰樺瓧绗︿覆 - const maxStrList = props.data.cols.map((item, index) => { + const maxStrList = tableCols.value.map((item, index) => { if (visibleColProp.value.includes(index + '')) { return item.title; } else { @@ -235,7 +408,7 @@ } }); // 杩斿洖鎵�鏈夋爣棰樺瓧绗︿覆闀垮害 - const maxLenList = props.data.cols.map((item, index) => { + const maxLenList = tableCols.value.map((item, index) => { if (visibleColProp.value.includes(index + '')) { return item.title.gblen(); } else { @@ -243,9 +416,10 @@ } }); // 璁$畻姣忎竴鍒楁渶闀跨殑鏍囬瀛楃涓插拰鏍囬瀛楃涓查暱搴� - for (const item of props.data.values) { + for (const item of tableValues.value) { item.map((subItem, index) => { - const subItemLen = subItem?.gblen(); + // subItem 鍙兘鏄� 鏁板瓧 + const subItemLen = (subItem + '')?.gblen(); if (maxLenList[index] < subItemLen) { maxLenList[index] = subItemLen; maxStrList[index] = subItem; @@ -280,7 +454,7 @@ // 褰撳墠宸叉弧瓒冲搴� let curWidth = 0; // 鎺掑ソ搴忕殑瀹藉害鍒楄〃 - const sortedWidthList = _.sortBy(maxWidthList, 'maxWidth'); + const sortedWidthList = sortBy(maxWidthList, 'maxWidth'); // 鍓╀綑瀹藉害 let restWidth = width; let notFitStartIndex = 1; @@ -323,9 +497,9 @@ if (width === 0 || height === 0) { return; } - if (props.data?.cols?.length > 0) { + if (tableCols.value.length > 0) { cellRowSpanMap.clear(); - props.data.values = buildGroupData(props.data.values); + // tableValues.value = buildGroupData(tableValues.value); calcColWidth(width); nextTick(() => { calcPagerHeight(); @@ -378,7 +552,7 @@ const pager = reactive({ index: 1, size: 10, - total: props.data?.values?.length ?? 0, + total: tableValues.value.length ?? 0, }); const calcCellHeight = (count, cellHeight) => { @@ -394,7 +568,7 @@ const headerHeight = tableRef.value.$el.querySelector('.el-table__header-wrapper').clientHeight; // 闄愬埗楂樺害 - const limitHeight =props.tableLimitHeight ??containerRef.value.clientHeight; + const limitHeight = props.tableLimitHeight ?? containerRef.value.clientHeight; // 姹� size // size*cellHeight + size*THICK_BORDER_WIDTH + headerHeight <= limitHeight; @@ -409,19 +583,23 @@ tableRef.value.doLayout(); // pager.index = 1; - chunkTableData.value = _.chunk(props.data.values ?? [], pager.size); + chunkTableData.value = chunk(tableValues.value ?? [], pager.size); }; //#endregion + +const tableData = computed(() => buildGroupData(chunkTableData.value[pager.index - 1])); const reloadTable = () => { // 閲嶆柊璁$畻瀹藉害 resizeEvent({ width: containerRef.value.clientWidth, height: containerRef.value.clientHeight }); }; + const colFilterChange = () => { // 閲嶆柊璁$畻瀹藉害 reloadTable(); + storeCols(colList.value); }; const tableHeight = ref(0.7 * document.body.clientHeight); @@ -445,43 +623,79 @@ }); //#region ====================== 琛ㄦ牸杩囨护鍙傛暟 ====================== - -const queryLoading = ref(false); -const queryUpdate = async (val: any, item: any) => { - const historyId = (props as any).originData.historyId; +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.StringInput) { + subPreVal.push({ + update: subCurVal.update, + value: subCurVal.value, + path: subCurVal.path, + }); + } else 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 if (subCurVal.type === RecordSetParamsType.Step) { + subPreVal.push({ + update: subCurVal.update, + value: subCurVal.step_value, + path: subCurVal.step_path, + }); + } + + return subPreVal; + }, []); + + preVal = preVal.concat(filter); + + return preVal; + }, []); + + return filterList; +}; +const queryLoading = ref(false); +let orderDimName = ''; +const queryUpdate = async (val?: any, item?: any) => { + const historyId = (props as any).originData.historyId; let res = null; - // 鏀瑰彉鍘熷鍊� - if (item.type === RecordSetTableType.StringInput) { - item.origin.value = val; + if (item) { + // 鏀瑰彉鍘熷鍊� + if (item.type === RecordSetParamsType.StringInput) { + item.origin.value = val; + } else if (item.type === RecordSetParamsType.TimeRange) { + item.origin.start_value = val[0]; + item.origin.end_value = val[1]; + } else if (item.type === RecordSetParamsType.Step) { + item.origin.step_value = val; + } } + + const filterList = getFilterList(); try { - // 鐩稿悓 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 === RecordSetTableType.StringInput) { - subPreVal.push({ - update: subCurVal.update, - value: subCurVal.value, - path: subCurVal.path, - }); - } - - return subPreVal; - }, []); - - preVal = preVal.concat(filter); - - return preVal; - }, []); const params = { history_id: historyId, // 鏌ヨ鍓嶅悗 agent_key 涓嶄細鍙� agent_key: props.data.agent_key, filter_json: JSON.stringify(filterList), + order_dim_name: orderDimName, }; res = await curveQuery(params); queryLoading.value = true; @@ -495,17 +709,21 @@ const debounceQueryUpdate = debounce(queryUpdate, 600); const handleQueryChange = async (val: any, item: any) => { - if (item.type === RecordSetTableType.StringInput) { + if (item.type === RecordSetParamsType.StringInput) { debounceQueryUpdate(val, item); + } else { + queryUpdate(val, item); } - - // queryUpdate(val,item) - - return; }; //#endregion - +const stepOptions = [ + { title: '5鍒嗛挓', value: '5 minutes' }, + { title: '10鍒嗛挓', value: '10 minutes' }, + { title: '鍗婂皬鏃�', value: '30 minutes' }, + { title: '1灏忔椂', value: '1 hours' }, + { title: '1澶�', value: '1 days' }, +]; const getVisibleParams = (data) => { // const visibleList = props.data?.params?.filter((item) => !item?.hide) ?? []; // index 浣滀负 id @@ -516,13 +734,33 @@ return item; }); const newList: any[] = []; + for (let index = 0; index < visibleList.length; index++) { const current = visibleList[index]; switch (current.type) { - case RecordSetTableType.StringInput: + case RecordSetParamsType.TimeRange: newList.push({ id: current.id, - type: RecordSetTableType.StringInput, + type: RecordSetParamsType.TimeRange, + origin: current, + value: [current.start_value, current.end_value], + title: current.title, + }); + break; + case RecordSetParamsType.Step: + newList.push({ + id: current.id, + type: RecordSetParamsType.Step, + origin: current, + value: current.step_value, + list: stepOptions, + title: current.title, + }); + break; + case RecordSetParamsType.StringInput: + newList.push({ + id: current.id, + type: RecordSetParamsType.StringInput, origin: current, value: current?.value ?? '', title: current.title, @@ -546,7 +784,7 @@ const currentItem = visibleParams.value.find((item) => item.id === index + ''); if (currentItem) { - if (newItem.type === RecordSetTableType.StringInput) { + if (newItem.type === RecordSetParamsType.StringInput) { currentItem.value = newItem.value; } } @@ -555,18 +793,24 @@ const resetPager = (data) => { pager.index = 1; - pager.total = data?.values?.length ?? 0; + pager.total = tableValues.value.length ?? 0; }; + +let needUpdateChart = false; const updateCurrent = (res) => { const title = res?.title; - const values = res?.values ?? []; props.data.title = title; - props.data.values = values; + tableValues.value = getTableValues(res); // 涓嶆洿鏂帮紝鍚庣鍘嬫牴娌¤繑鍥� // updateFilterValue(res); resetPager(res); reloadTable(); + if (showMode.value === DisplayModeType.List) { + needUpdateChart = true; + } else { + updateChart(); + } }; // 鏌ヨ const updateAll = (triggerIndex, res) => { @@ -599,9 +843,302 @@ updateCurrent(newSummary); }; +const updateIndexSummary = (summary) => { + updateCurrent(summary?.[props.summaryIndex]); +}; +//#region ====================== 妯″紡鍒囨崲 ====================== +const showMode = ref(DisplayModeType.List); +const modeChangeOrder = [DisplayModeType.List, DisplayModeType.Chart]; +const chartRef = ref<HTMLDivElement>(null); +const chartInstance = shallowRef<echarts.ECharts>(null); + +const debounceResizeChart = debounce(({ width, height }) => { + if (width === 0 || height === 0) return; + chartInstance.value?.resize({ + width, + height, + }); +}); + +let groupTitle; + +let activeChartType: ChartTypeEnum = ChartTypeEnum.Bar; +const getChartData = (values: Array<any[]>) => { + // 鎺掗櫎鍚堣琛岋紱 + const groupValues = values.slice(0, values.length - 1); + const excludeIndex = [rateColIndex]; + + const groupCols = tableCols.value.filter((item, index) => !excludeIndex.includes(index) && index !== valueColIndex); + // 鍙栨渶鍚庝竴涓綔涓� title + groupTitle = groupCols[groupCols.length - 1].title; + + const data = groupValues.map((item) => { + const groupName = item.filter((item, index) => !excludeIndex.includes(index) && index !== valueColIndex).join('_'); + const value = item[valueColIndex]; + return { + value, + name: groupName, + }; + }); + + return data; +}; + +const getCommonOption = () => { + return { + title: { + text: props.data.title, + left: 'center', + textStyle: { + fontSize: 14, + }, + }, + + legend: { + type: 'scroll', + // orient:'vertical', + top: 33, + left: 'center', + }, + tooltip: { + trigger: 'item', + valueFormatter: (value) => `${value} / ${toPercent(sumValue === 0 ? 0 : (value as number) / sumValue, true, 2)}`, + }, + } as echarts.EChartsOption; +}; + +const getPieOption = (chartData: any[]) => { + // const labelFormatter = chartData.length > 9 ? '{b}' : '{b}\n{c}/{d}%'; + const labelFormatter = '{b}: {c} / {d}%'; + return { + series: [ + { + type: 'pie', + radius: ['20%', '55%'], + center: ['50%', '55%'], + label: { + show: true, + // formatter: '{b}: {c}({d}%)', //鑷畾涔夋樉绀烘牸寮�(b:name, c:value, d:鐧惧垎姣�) + }, + bottom: 0, + itemStyle: { + normal: { + label: { + formatter: labelFormatter, + // formatter: '{b}', + }, + borderColor: '#ffffff', + borderWidth: 5, + }, + }, + + data: chartData, + }, + ], + } as echarts.EChartsOption; +}; + +const getBarOption = (chartData: any[]) => { + const barData = chartData.map((item) => item.value); + const right = groupTitle + ? { + right: 60, + } + : { + right: 30, + }; + const xData = chartData.map((item) => item.name); + return { + grid: { + top: 65, + left: 30, + ...right, + bottom: 0, + containLabel: true, + }, + xAxis: { + name: groupTitle, + type: 'category', + data: xData, + axisLabel: { + fontSize: 12, + // formatter: function (value) { + // if (value.length > 6) { + // return value.substr(0, 6) + '...'; + // } else { + // return value; + // } + // }, + width: 86, + overflow: 'truncate', + // nameTruncate: { + // maxWidth: 52, + // }, + rotate: 30, + }, + nameTextStyle: { + padding: [-15, 0, 0, -10], + align: 'left', + verticalAlign: 'top', + // color: '#fff', + }, + nameTruncate: { + maxWidth: 52, + }, + }, + yAxis: { + name: tableCols.value[valueColIndex].title, + type: 'value', + axisLabel: { + formatter: axisLabelFormatter, + }, + }, + series: { + type: 'bar', + data: barData, + label: { + show: true, + position: 'outside', + formatter: (params) => { + const value = params.value; + return `${value} / ${toPercent(sumValue === 0 ? 0 : (value as number) / sumValue, true, 2)}`; + }, + fontSize: 10, + }, + }, + dataZoom: { + type: 'inside', + }, + } as echarts.EChartsOption; +}; + +const getChartOption = (chartData: any[]) => { + let typeOption: echarts.EChartsOption = {}; + if (activeChartType === ChartTypeEnum.Pie) { + typeOption = getPieOption(chartData); + } else if (activeChartType === ChartTypeEnum.Bar) { + typeOption = getBarOption(chartData); + } + + const option: echarts.EChartsOption = { + ...getCommonOption(), + + toolbox: { + show: true, + feature: { + myBar: { + title: '杞寲涓烘煴鐘跺浘', + show: true, + icon: PATH_ICON.bar, + onclick: () => { + activeChartType = ChartTypeEnum.Bar; + chartInstance.value.setOption( + { + ...getCommonOption(), + + toolbox: option.toolbox, + ...getBarOption(chartData), + }, + { + notMerge: true, + } + ); + }, + }, + + myPie: { + title: '杞寲涓洪ゼ鐘跺浘', + show: true, + icon: PATH_ICON.pie, + onclick: () => { + activeChartType = ChartTypeEnum.Pie; + chartInstance.value.setOption( + { + ...getCommonOption(), + toolbox: option.toolbox, + ...getPieOption(chartData), + }, + { + notMerge: true, + } + ); + }, + }, + + saveAsImage: {}, + }, + }, + ...typeOption, + }; + + return option; +}; + +const updateChart = () => { + const chartData = getChartData(tableValues.value); + + const option = getChartOption(chartData); + nextTick(() => { + if (!chartInstance.value) { + chartInstance.value = echarts.init(chartRef.value); + } + + chartInstance.value.setOption(option, { + notMerge: true, + }); + }); +}; +const displayModeChange = (val: DisplayModeType) => { + if (val === DisplayModeType.Chart) { + if (!chartInstance.value || needUpdateChart) { + updateChart(); + needUpdateChart = false; + } + } +}; +//#endregion + +const orderMap = new Map(); +const sortChange = ({ column, prop, order }) => { + // 鎭㈠鍘熺姸锛屾洿鏂板悗鍐嶆樉绀烘帓搴忕姸鎬� + const curOrder = orderMap.get(prop) ?? null; + column.order = curOrder; + + let sendOrder; + if (order === 'descending') { + sendOrder = 'desc'; + } else if (order === 'ascending') { + sendOrder = 'asc'; + } else { + sendOrder = ''; + } + const colName = colList.value[prop].name; + const sendOrderName = sendOrder ? `${colName},${sendOrder}` : ''; + orderDimName = sendOrderName; + + queryUpdate().then(() => { + orderMap.set(prop, order); + column.order = order; + }); +}; + +// 鍦板浘妯″紡灞曠ず +const showMode2 = ref(DisplayModeType2.Map); +const modeChangeOrder2 = [DisplayModeType2.Map, DisplayModeType2.List]; + +const displayModeChange2 = (val: DisplayModeType2) => { + if (val === DisplayModeType2.Map) { + //if (!chartInstance.value || needUpdateChart) { + //updateChart(); + //needUpdateChart = false; + //} + } +}; defineExpose({ updateAll, + updateCurrent, + updateIndexSummary, }); </script> <style scoped lang="scss"> -- Gitblit v1.9.3