数据查询新增功能
This commit is contained in:
@@ -145,6 +145,59 @@ export function deleteReport(id: number) {
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取某业务下所有报表(不分页,供下拉选择) */
|
||||
export function listAllReports(businessCode: string) {
|
||||
return request({
|
||||
url: '/data-engine/report/reports',
|
||||
method: 'get',
|
||||
params: { businessCode, pageNum: 1, pageSize: 9999 },
|
||||
});
|
||||
}
|
||||
|
||||
// --- 查询相关类型 ---
|
||||
|
||||
export interface ReportFilter {
|
||||
fieldCode: string;
|
||||
operator: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface ReportIndicator {
|
||||
fieldCode: string;
|
||||
aggregate: string;
|
||||
alias: string;
|
||||
}
|
||||
|
||||
export interface ReportOrderBy {
|
||||
fieldCode: string;
|
||||
direction: 'ASC' | 'DESC';
|
||||
}
|
||||
|
||||
export interface ReportQueryParams {
|
||||
businessCode: string;
|
||||
reportCode: string;
|
||||
dimensions: string[];
|
||||
indicators: ReportIndicator[];
|
||||
filters: ReportFilter[];
|
||||
timeRange: {
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
};
|
||||
timeGroup: string;
|
||||
orderBy: ReportOrderBy[];
|
||||
page: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
/** 数据查询 */
|
||||
export function reportQuery(data: ReportQueryParams) {
|
||||
return request({
|
||||
url: '/data-engine/report/query',
|
||||
method: 'post',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/** 按天回刷(支持单天或日期范围) */
|
||||
export function execDailyBackfill(data: { businessCode: string; reportCode: string; startDate: string; endDate: string }) {
|
||||
return request({
|
||||
|
||||
@@ -0,0 +1,627 @@
|
||||
<template>
|
||||
<div class="report-query-container">
|
||||
<!-- 选择业务和报表 -->
|
||||
<el-card shadow="hover" class="mb15">
|
||||
<el-form :inline="true">
|
||||
<el-form-item label="业务">
|
||||
<el-select v-model="queryForm.businessCode" placeholder="请选择业务" style="width: 200px" @change="onBusinessChange">
|
||||
<el-option v-for="biz in businesses" :key="biz.businessCode" :label="biz.businessName" :value="biz.businessCode" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="报表">
|
||||
<el-select v-model="queryForm.reportCode" placeholder="请选择报表" style="width: 260px" @change="onReportChange" :loading="reportLoading">
|
||||
<el-option v-for="r in reports" :key="r.reportCode" :label="r.reportName" :value="r.reportCode" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 查询配置区 -->
|
||||
<template v-if="reportLoaded">
|
||||
<el-card shadow="hover" class="mb15">
|
||||
<template #header><span>查询配置</span></template>
|
||||
<el-form :model="queryForm" label-width="100px">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="时间范围" required>
|
||||
<el-date-picker
|
||||
v-model="timeRange"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="时间粒度">
|
||||
<el-select v-model="queryForm.timeGroup" style="width: 100%">
|
||||
<el-option label="按天" value="day" />
|
||||
<el-option label="按周" value="week" />
|
||||
<el-option label="按月" value="month" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 维度 -->
|
||||
<el-form-item label="统计维度">
|
||||
<el-checkbox-group v-model="queryForm.dimensions" class="field-checkbox-group">
|
||||
<el-checkbox
|
||||
v-for="f in dimensionFields"
|
||||
:key="f.fieldCode"
|
||||
:label="f.fieldCode"
|
||||
>
|
||||
{{ f.fieldName || f.fieldCode }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
<div v-if="dimensionFields.length === 0" class="field-empty-hint">该报表无可用的维度字段</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 指标 -->
|
||||
<el-form-item label="统计指标">
|
||||
<div class="indicator-list" v-if="indicatorFields.length > 0">
|
||||
<div v-for="(f, idx) in indicatorFields" :key="f.fieldCode" class="indicator-row">
|
||||
<el-checkbox
|
||||
v-model="indicatorChecked[f.fieldCode]"
|
||||
:label="f.fieldName || f.fieldCode"
|
||||
@change="(val: boolean) => onIndicatorCheck(f.fieldCode, val)"
|
||||
style="min-width: 120px"
|
||||
/>
|
||||
<template v-if="indicatorChecked[f.fieldCode]">
|
||||
<el-select
|
||||
v-model="indicatorAggs[f.fieldCode].aggregate"
|
||||
style="width: 130px; margin-left: 8px"
|
||||
placeholder="聚合方式"
|
||||
>
|
||||
<el-option v-for="agg in aggregateOptions" :key="agg.value" :label="agg.label" :value="agg.value" />
|
||||
</el-select>
|
||||
<el-input
|
||||
v-model="indicatorAggs[f.fieldCode].alias"
|
||||
style="width: 130px; margin-left: 8px"
|
||||
placeholder="显示别名"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="field-empty-hint">该报表无可用的指标字段</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 筛选条件 -->
|
||||
<el-form-item label="筛选条件">
|
||||
<div class="filter-list">
|
||||
<div v-for="(f, idx) in filters" :key="idx" class="filter-row">
|
||||
<el-select v-model="f.fieldCode" style="width: 150px" placeholder="字段" @change="onFilterFieldChange(idx)">
|
||||
<el-option v-for="qf in queryableFields" :key="qf.fieldCode" :label="qf.fieldName || qf.fieldCode" :value="qf.fieldCode" />
|
||||
</el-select>
|
||||
<el-select v-model="f.operator" style="width: 100px; margin-left: 6px" placeholder="运算符">
|
||||
<el-option v-for="op in filterOperators" :key="op.value" :label="op.label" :value="op.value" />
|
||||
</el-select>
|
||||
<el-input v-model="f.value" style="width: 160px; margin-left: 6px" placeholder="值" />
|
||||
<el-button type="danger" :icon="Delete" circle size="small" style="margin-left: 6px" @click="removeFilter(idx)" />
|
||||
</div>
|
||||
<el-button size="small" @click="addFilter" :disabled="queryableFields.length === 0">+ 添加筛选</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 排序 -->
|
||||
<el-form-item label="排序">
|
||||
<div class="order-list">
|
||||
<div v-for="(o, idx) in queryForm.orderBy" :key="idx" class="order-row">
|
||||
<el-select v-model="o.fieldCode" style="width: 150px" placeholder="排序字段">
|
||||
<el-option v-for="qf in sortableFields" :key="qf.fieldCode" :label="qf.fieldName || qf.fieldCode" :value="qf.fieldCode" />
|
||||
</el-select>
|
||||
<el-select v-model="o.direction" style="width: 100px; margin-left: 6px">
|
||||
<el-option label="降序" value="DESC" />
|
||||
<el-option label="升序" value="ASC" />
|
||||
</el-select>
|
||||
<el-button type="danger" :icon="Delete" circle size="small" style="margin-left: 6px" @click="removeOrder(idx)" />
|
||||
</div>
|
||||
<el-button size="small" @click="addOrder" :disabled="sortableFields.length === 0">+ 添加排序</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" size="default" @click="handleQuery" :loading="queryLoading">
|
||||
<el-icon><ele-Search /></el-icon> 查询
|
||||
</el-button>
|
||||
<el-button size="default" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 查询结果 -->
|
||||
<el-card shadow="hover" v-if="queryDone">
|
||||
<template #header>
|
||||
<div class="result-header">
|
||||
<span>查询结果(共 {{ queryResult.total }} 条)</span>
|
||||
<el-radio-group v-model="viewMode" size="small">
|
||||
<el-radio-button value="table">数据列表</el-radio-button>
|
||||
<el-radio-button value="chart">图表展示</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 数据列表 -->
|
||||
<template v-if="viewMode === 'table'">
|
||||
<el-table :data="queryResult.list" border stripe v-loading="queryLoading" style="width: 100%" empty-text="暂无数据">
|
||||
<el-table-column v-for="col in queryResult.columns" :key="col" :prop="col" :label="col" show-overflow-tooltip min-width="120" />
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="queryResult.total > 0"
|
||||
:total="queryResult.total"
|
||||
v-model:page="queryForm.page"
|
||||
v-model:limit="queryForm.pageSize"
|
||||
@pagination="handleQuery"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 图表展示 -->
|
||||
<template v-if="viewMode === 'chart'">
|
||||
<div class="chart-tip" v-if="!chartReady">
|
||||
<el-alert title="图表展示需要至少选择一个维度和一个指标" type="info" show-icon :closable="false" />
|
||||
</div>
|
||||
<div ref="chartRef" class="chart-container" v-loading="queryLoading"></div>
|
||||
</template>
|
||||
</el-card>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default { name: 'reportEngineQuery' };
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch, nextTick } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Delete } from '@element-plus/icons-vue';
|
||||
import * as echarts from 'echarts';
|
||||
import {
|
||||
listBusinesses,
|
||||
listAllReports,
|
||||
getReport,
|
||||
reportQuery,
|
||||
} from '/@/api/report-engine/report';
|
||||
import type {
|
||||
FieldDefine,
|
||||
ReportFilter,
|
||||
ReportOrderBy,
|
||||
} from '/@/api/report-engine/report';
|
||||
|
||||
// ========== 常量 ==========
|
||||
|
||||
const AGGREGATE_OPTIONS = [
|
||||
{ value: 'SUM', label: 'SUM (求和)' },
|
||||
{ value: 'COUNT', label: 'COUNT (计数)' },
|
||||
{ value: 'AVG', label: 'AVG (平均)' },
|
||||
{ value: 'MAX', label: 'MAX (最大)' },
|
||||
{ value: 'MIN', label: 'MIN (最小)' },
|
||||
];
|
||||
|
||||
const FILTER_OPERATORS = [
|
||||
{ value: '=', label: '=' },
|
||||
{ value: '!=', label: '!=' },
|
||||
{ value: '>', label: '>' },
|
||||
{ value: '>=', label: '>=' },
|
||||
{ value: '<', label: '<' },
|
||||
{ value: '<=', label: '<=' },
|
||||
{ value: 'LIKE', label: 'LIKE' },
|
||||
{ value: 'IN', label: 'IN' },
|
||||
];
|
||||
|
||||
// ========== 状态 ==========
|
||||
|
||||
const businesses = ref<any[]>([]);
|
||||
const reports = ref<any[]>([]);
|
||||
const reportLoading = ref(false);
|
||||
const reportLoaded = ref(false);
|
||||
const queryLoading = ref(false);
|
||||
const queryDone = ref(false);
|
||||
const viewMode = ref<'table' | 'chart'>('table');
|
||||
const chartRef = ref<HTMLDivElement>();
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
|
||||
/** 当前选中报表的所有字段 */
|
||||
const reportFields = ref<FieldDefine[]>([]);
|
||||
|
||||
// 维度字段(role === DIMENSION)
|
||||
const dimensionFields = computed(() =>
|
||||
reportFields.value.filter((f) => f.fieldRole === 'DIMENSION')
|
||||
);
|
||||
|
||||
// 指标字段(role === INDICATOR)
|
||||
const indicatorFields = computed(() =>
|
||||
reportFields.value.filter((f) => f.fieldRole === 'INDICATOR')
|
||||
);
|
||||
|
||||
// 可筛选字段
|
||||
const queryableFields = computed(() =>
|
||||
reportFields.value.filter((f) => f.isFilterable !== false || f.isQueryable)
|
||||
);
|
||||
|
||||
// 可排序字段
|
||||
const sortableFields = computed(() =>
|
||||
reportFields.value.filter((f) => f.isSortable !== false || f.fieldRole === 'DIMENSION')
|
||||
);
|
||||
|
||||
const aggregateOptions = AGGREGATE_OPTIONS;
|
||||
const filterOperators = FILTER_OPERATORS;
|
||||
|
||||
/** 指标选中状态 */
|
||||
const indicatorChecked = reactive<Record<string, boolean>>({});
|
||||
/** 指标聚合配置 */
|
||||
const indicatorAggs = reactive<Record<string, { aggregate: string; alias: string }>>({});
|
||||
|
||||
/** 筛选条件列表 */
|
||||
const filters = ref<ReportFilter[]>([]);
|
||||
|
||||
/** 时间范围绑定(内部用数组) */
|
||||
const timeRange = ref<string[]>([]);
|
||||
|
||||
/** 查询表单 */
|
||||
const queryForm = reactive({
|
||||
businessCode: '',
|
||||
reportCode: '',
|
||||
dimensions: [] as string[],
|
||||
timeGroup: 'day',
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
orderBy: [] as ReportOrderBy[],
|
||||
});
|
||||
|
||||
/** 查询结果 */
|
||||
const queryResult = reactive({
|
||||
list: [] as Record<string, any>[],
|
||||
columns: [] as string[],
|
||||
total: 0,
|
||||
});
|
||||
|
||||
/** 图表数据是否可用 */
|
||||
const chartReady = computed(() => {
|
||||
return queryForm.dimensions.length > 0 && Object.values(indicatorChecked).some(Boolean);
|
||||
});
|
||||
|
||||
// ========== 初始化 ==========
|
||||
|
||||
const initBusinesses = async () => {
|
||||
try {
|
||||
const res = await listBusinesses();
|
||||
businesses.value = res.data?.list || res.data || [];
|
||||
} catch (_e) {
|
||||
ElMessage.error('获取业务列表失败');
|
||||
}
|
||||
};
|
||||
|
||||
initBusinesses();
|
||||
|
||||
// ========== 业务/报表切换 ==========
|
||||
|
||||
const onBusinessChange = async (code: string) => {
|
||||
queryForm.reportCode = '';
|
||||
reportLoaded.value = false;
|
||||
queryDone.value = false;
|
||||
reports.value = [];
|
||||
if (!code) return;
|
||||
|
||||
reportLoading.value = true;
|
||||
try {
|
||||
const res = await listAllReports(code);
|
||||
const raw = res.data;
|
||||
reports.value = Array.isArray(raw) ? raw : raw?.list || raw?.records || [];
|
||||
} catch (_e) {
|
||||
reports.value = [];
|
||||
ElMessage.error('获取报表列表失败');
|
||||
} finally {
|
||||
reportLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onReportChange = async (code: string) => {
|
||||
reportLoaded.value = false;
|
||||
queryDone.value = false;
|
||||
if (!code) return;
|
||||
|
||||
// 从报表列表中找到该报表的 id
|
||||
const row = reports.value.find((r) => r.reportCode === code);
|
||||
if (!row?.id) {
|
||||
ElMessage.warning('未找到报表信息');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await getReport(row.id);
|
||||
const raw = res.data || {};
|
||||
const detail = raw.report || raw.data || raw;
|
||||
reportFields.value = detail.fields || [];
|
||||
|
||||
// 重置查询配置
|
||||
resetQueryState();
|
||||
|
||||
// 默认选中所有维度
|
||||
queryForm.dimensions = dimensionFields.value.map((f) => f.fieldCode);
|
||||
|
||||
// 默认选中所有指标
|
||||
indicatorFields.value.forEach((f) => {
|
||||
indicatorChecked[f.fieldCode] = true;
|
||||
indicatorAggs[f.fieldCode] = {
|
||||
aggregate: f.defaultAggregate || 'SUM',
|
||||
alias: f.fieldName || f.fieldCode,
|
||||
};
|
||||
});
|
||||
|
||||
reportLoaded.value = true;
|
||||
} catch (_e) {
|
||||
ElMessage.error('获取报表字段信息失败');
|
||||
}
|
||||
};
|
||||
|
||||
// ========== 指标勾选 ==========
|
||||
|
||||
const onIndicatorCheck = (fieldCode: string, checked: boolean) => {
|
||||
if (checked) {
|
||||
const f = reportFields.value.find((r) => r.fieldCode === fieldCode);
|
||||
indicatorAggs[fieldCode] = {
|
||||
aggregate: f?.defaultAggregate || 'SUM',
|
||||
alias: f?.fieldName || fieldCode,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// ========== 筛选条件 ==========
|
||||
|
||||
const addFilter = () => {
|
||||
filters.value.push({ fieldCode: '', operator: '=', value: '' });
|
||||
};
|
||||
|
||||
const removeFilter = (idx: number) => {
|
||||
filters.value.splice(idx, 1);
|
||||
};
|
||||
|
||||
const onFilterFieldChange = (idx: number) => {
|
||||
// 清空值,方便用户输入
|
||||
filters.value[idx].value = '';
|
||||
};
|
||||
|
||||
// ========== 排序 ==========
|
||||
|
||||
const addOrder = () => {
|
||||
queryForm.orderBy.push({ fieldCode: '', direction: 'DESC' });
|
||||
};
|
||||
|
||||
const removeOrder = (idx: number) => {
|
||||
queryForm.orderBy.splice(idx, 1);
|
||||
};
|
||||
|
||||
// ========== 查询 ==========
|
||||
|
||||
const buildQueryParams = () => {
|
||||
const dimensions = [...queryForm.dimensions];
|
||||
|
||||
// 收集已勾选的指标
|
||||
const indicators: { fieldCode: string; aggregate: string; alias: string }[] = [];
|
||||
indicatorFields.value.forEach((f) => {
|
||||
if (indicatorChecked[f.fieldCode]) {
|
||||
indicators.push({
|
||||
fieldCode: f.fieldCode,
|
||||
aggregate: indicatorAggs[f.fieldCode]?.aggregate || 'SUM',
|
||||
alias: indicatorAggs[f.fieldCode]?.alias || f.fieldName || f.fieldCode,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const validFilters = filters.value.filter((f) => f.fieldCode && f.value);
|
||||
const validOrderBy = queryForm.orderBy.filter((o) => o.fieldCode);
|
||||
|
||||
return {
|
||||
businessCode: queryForm.businessCode,
|
||||
reportCode: queryForm.reportCode,
|
||||
dimensions,
|
||||
indicators,
|
||||
filters: validFilters,
|
||||
timeRange: {
|
||||
startDate: timeRange.value?.[0] || '',
|
||||
endDate: timeRange.value?.[1] || '',
|
||||
},
|
||||
timeGroup: queryForm.timeGroup,
|
||||
orderBy: validOrderBy,
|
||||
page: queryForm.page,
|
||||
pageSize: queryForm.pageSize,
|
||||
};
|
||||
};
|
||||
|
||||
const handleQuery = async () => {
|
||||
if (!queryForm.businessCode || !queryForm.reportCode) {
|
||||
ElMessage.warning('请选择业务和报表');
|
||||
return;
|
||||
}
|
||||
if (!timeRange.value || timeRange.value.length !== 2) {
|
||||
ElMessage.warning('请选择时间范围');
|
||||
return;
|
||||
}
|
||||
|
||||
queryLoading.value = true;
|
||||
queryDone.value = true;
|
||||
try {
|
||||
const params = buildQueryParams();
|
||||
const res = await reportQuery(params);
|
||||
const raw = res.data || {};
|
||||
|
||||
// 适配多种返回格式
|
||||
const list = raw.list || raw.records || raw.rows || [];
|
||||
const total = raw.total ?? list.length;
|
||||
|
||||
// 从接口获取列信息,或从数据推断
|
||||
const columns = raw.columns || raw.header || [];
|
||||
if (columns.length === 0 && list.length > 0) {
|
||||
queryResult.columns = Object.keys(list[0]);
|
||||
} else {
|
||||
queryResult.columns = columns;
|
||||
}
|
||||
|
||||
queryResult.list = list;
|
||||
queryResult.total = total;
|
||||
|
||||
// 切换到图表时渲染
|
||||
if (viewMode.value === 'chart') {
|
||||
nextTick(() => renderChart());
|
||||
}
|
||||
} catch (_e) {
|
||||
queryResult.list = [];
|
||||
queryResult.columns = [];
|
||||
queryResult.total = 0;
|
||||
ElMessage.error('查询失败');
|
||||
} finally {
|
||||
queryLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const resetQuery = () => {
|
||||
resetQueryState();
|
||||
timeRange.value = [];
|
||||
queryForm.page = 1;
|
||||
queryDone.value = false;
|
||||
};
|
||||
|
||||
const resetQueryState = () => {
|
||||
queryForm.dimensions = [];
|
||||
queryForm.orderBy = [];
|
||||
queryForm.timeGroup = 'day';
|
||||
queryForm.pageSize = 20;
|
||||
queryForm.page = 1;
|
||||
filters.value = [];
|
||||
// 清空指标状态
|
||||
Object.keys(indicatorChecked).forEach((k) => delete indicatorChecked[k]);
|
||||
Object.keys(indicatorAggs).forEach((k) => delete indicatorAggs[k]);
|
||||
};
|
||||
|
||||
// ========== 图表渲染 ==========
|
||||
|
||||
watch(viewMode, (mode) => {
|
||||
if (mode === 'chart' && queryDone.value && queryResult.list.length > 0) {
|
||||
nextTick(() => renderChart());
|
||||
}
|
||||
if (mode === 'table') {
|
||||
disposeChart();
|
||||
}
|
||||
});
|
||||
|
||||
const renderChart = () => {
|
||||
if (!chartRef.value || queryResult.list.length === 0) return;
|
||||
if (!chartReady.value) {
|
||||
disposeChart();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!chartInstance) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
}
|
||||
|
||||
const dimensions = queryForm.dimensions;
|
||||
const dimField = dimensions[0] || queryResult.columns[0];
|
||||
|
||||
// x 轴:第一个维度
|
||||
const xData = queryResult.list.map((row) => row[dimField] ?? '');
|
||||
|
||||
// 系列:已勾选的指标
|
||||
// 注意:API 返回的数据中,指标字段的 key 使用的是 alias,而非 fieldCode
|
||||
const series: echarts.SeriesOption[] = [];
|
||||
indicatorFields.value.forEach((f) => {
|
||||
if (indicatorChecked[f.fieldCode]) {
|
||||
const alias = indicatorAggs[f.fieldCode]?.alias || f.fieldName || f.fieldCode;
|
||||
series.push({
|
||||
name: alias,
|
||||
type: 'bar',
|
||||
data: queryResult.list.map((row) => {
|
||||
const val = Number(row[alias]);
|
||||
return isNaN(val) ? row[alias] : val;
|
||||
}),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
chartInstance.setOption({
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: { type: 'scroll', bottom: 0 },
|
||||
grid: { left: '3%', right: '4%', bottom: '20%', containLabel: true },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: xData,
|
||||
axisLabel: { rotate: xData.length > 10 ? 45 : 0 },
|
||||
},
|
||||
yAxis: { type: 'value' },
|
||||
series,
|
||||
});
|
||||
|
||||
chartInstance.resize();
|
||||
};
|
||||
|
||||
const disposeChart = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
};
|
||||
|
||||
// ========== 窗口 resize ==========
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
if (chartInstance) chartInstance.resize();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.report-query-container {
|
||||
.field-checkbox-group {
|
||||
.el-checkbox {
|
||||
margin-right: 16px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.field-empty-hint {
|
||||
color: var(--el-text-color-placeholder);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.indicator-list {
|
||||
width: 100%;
|
||||
|
||||
.indicator-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-list,
|
||||
.order-list {
|
||||
width: 100%;
|
||||
|
||||
.filter-row,
|
||||
.order-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.result-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
.chart-tip {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user