display all data in statistics & analysis and hide percentages with values below zero

This commit is contained in:
MaysWind
2026-01-16 01:20:29 +08:00
parent 83a34ae322
commit e304f4d3fa
9 changed files with 67 additions and 51 deletions
+16 -10
View File
@@ -13,8 +13,9 @@ export interface CommonPieChartDataItem {
name: string; name: string;
displayName: string; displayName: string;
value: number; value: number;
actualValue: number;
percent: number; percent: number;
actualPercent: number; paintPercent: number;
color: ColorStyleValue; color: ColorStyleValue;
sourceItem: Record<string, unknown>; sourceItem: Record<string, unknown>;
displayPercent?: string; displayPercent?: string;
@@ -30,7 +31,6 @@ export interface CommonPieChartProps {
percentField?: string; percentField?: string;
colorField?: string; colorField?: string;
hiddenField?: string; hiddenField?: string;
minValidPercent?: number;
amountValue?: boolean; amountValue?: boolean;
defaultCurrency?: string; defaultCurrency?: string;
showValue?: boolean; showValue?: boolean;
@@ -55,32 +55,38 @@ export function usePieChartBase(props: CommonPieChartProps) {
} }
const validItems: CommonPieChartDataItem[] = []; const validItems: CommonPieChartDataItem[] = [];
let accumulatedPaintPercent: number = 0;
for (const item of props.items) { for (const item of props.items) {
const value = item[props.valueField]; const value = item[props.valueField];
const percent = props.percentField ? item[props.percentField] : -1; const percent = props.percentField ? item[props.percentField] : -1;
if (isNumber(value) && value > 0 && if (isNumber(value) &&
(!props.hiddenField || !item[props.hiddenField]) && (!props.hiddenField || !item[props.hiddenField])) {
(!props.minValidPercent || value / totalValidValue > props.minValidPercent)) {
const finalItem: CommonPieChartDataItem = { const finalItem: CommonPieChartDataItem = {
id: (props.idField && item[props.idField]) ? item[props.idField] as string : item[props.nameField] as string, id: (props.idField && item[props.idField]) ? item[props.idField] as string : item[props.nameField] as string,
name: (props.idField && item[props.idField]) ? item[props.idField] as string : item[props.nameField] as string, name: (props.idField && item[props.idField]) ? item[props.idField] as string : item[props.nameField] as string,
displayName: item[props.nameField] as string, displayName: item[props.nameField] as string,
value: value, value: value > 0 ? value : 0,
percent: (isNumber(percent) && percent >= 0) ? percent : (value / totalValidValue * 100), actualValue: value,
actualPercent: value / totalValidValue, percent: (isNumber(percent) && percent >= 0) ? percent : (value > 0 ? value / totalValidValue * 100 : 0),
paintPercent: value > 0 ? value / totalValidValue : 0,
color: getDisplayColor((props.colorField && item[props.colorField]) ? item[props.colorField] as ColorValue : DEFAULT_CHART_COLORS[validItems.length % DEFAULT_CHART_COLORS.length]), color: getDisplayColor((props.colorField && item[props.colorField]) ? item[props.colorField] as ColorValue : DEFAULT_CHART_COLORS[validItems.length % DEFAULT_CHART_COLORS.length]),
sourceItem: item sourceItem: item
}; };
finalItem.displayPercent = formatPercentToLocalizedNumerals(finalItem.percent, 2, '&lt;0.01'); accumulatedPaintPercent += finalItem.paintPercent;
finalItem.displayValue = props.amountValue ? formatAmountToLocalizedNumeralsWithCurrency(finalItem.value, props.defaultCurrency) : finalItem.value.toString(); finalItem.displayPercent = formatPercentToLocalizedNumerals(finalItem.percent, 2, '<0.01');
finalItem.displayValue = props.amountValue ? formatAmountToLocalizedNumeralsWithCurrency(value, props.defaultCurrency) : value.toString();
validItems.push(finalItem); validItems.push(finalItem);
} }
} }
if (validItems.length > 0) {
validItems[validItems.length - 1]!.paintPercent += 1 - accumulatedPaintPercent;
}
return validItems; return validItems;
}); });
@@ -267,7 +267,7 @@ const chartOptions = computed<object>(() => {
} }
if (isNumber(dataItem.percent) && dataItem.percent > 0) { if (isNumber(dataItem.percent) && dataItem.percent > 0) {
const displayPercent = formatPercentToLocalizedNumerals(dataItem.percent, 2, '&lt;0.01'); const displayPercent = formatPercentToLocalizedNumerals(dataItem.percent, 2, '<0.01');
tooltip += `<span class="ms-1" style="float: inline-end">(${displayPercent})</span>`; tooltip += `<span class="ms-1" style="float: inline-end">(${displayPercent})</span>`;
} }
+1 -1
View File
@@ -396,7 +396,7 @@ function getItemName(name: string): string {
function getDisplayValue(value: number): string { function getDisplayValue(value: number): string {
if (props.oneHundredPercentStacked) { if (props.oneHundredPercentStacked) {
return formatPercentToLocalizedNumerals(value, 2, '&lt;0.01'); return formatPercentToLocalizedNumerals(value, 2, '<0.01');
} }
if (props.amountValue) { if (props.amountValue) {
+6 -3
View File
@@ -142,11 +142,14 @@ const chartOptions = computed<object>(() => {
tooltip += `<div class="d-inline-flex">${name}</div><br/>`; tooltip += `<div class="d-inline-flex">${name}</div><br/>`;
} }
if (props.showValue && props.showPercent) { const showValue = props.showValue;
const showPercent = props.showPercent && params.value && (params.value as number) > 0;
if (showValue && showPercent) {
tooltip += `<div class="d-inline-flex"><span>${value}</span><span class="ms-1">(${percent})</span></div>`; tooltip += `<div class="d-inline-flex"><span>${value}</span><span class="ms-1">(${percent})</span></div>`;
} else if (props.showValue && !props.showPercent) { } else if (showValue && !showPercent) {
tooltip += `<div class="d-inline-flex">${value}</div>`; tooltip += `<div class="d-inline-flex">${value}</div>`;
} else if (!props.showValue && props.showPercent) { } else if (!showValue && showPercent) {
tooltip += `<div class="d-inline-flex">${percent}</div>`; tooltip += `<div class="d-inline-flex">${percent}</div>`;
} }
+11 -10
View File
@@ -37,7 +37,6 @@ const props = defineProps<{
percentField?: string; percentField?: string;
colorField?: string; colorField?: string;
hiddenField?: string; hiddenField?: string;
minValidPercent?: number;
amountValue?: boolean; amountValue?: boolean;
defaultCurrency?: string; defaultCurrency?: string;
showValue?: boolean; showValue?: boolean;
@@ -74,14 +73,13 @@ const radarData = computed<RadarChartData>(() => {
const value = item[props.valueField]; const value = item[props.valueField];
const percent = props.percentField ? item[props.percentField] : -1; const percent = props.percentField ? item[props.percentField] : -1;
if (isNumber(value) && value > 0 && if (isNumber(value) &&
(!props.hiddenField || !item[props.hiddenField]) && (!props.hiddenField || !item[props.hiddenField])) {
(!props.minValidPercent || value / totalValidValue > props.minValidPercent)) {
const name = item[props.nameField] as string; const name = item[props.nameField] as string;
const color = getDisplayColor((props.colorField && item[props.colorField]) ? item[props.colorField] as ColorValue : DEFAULT_CHART_COLORS[indicators.length % DEFAULT_CHART_COLORS.length]); const color = getDisplayColor((props.colorField && item[props.colorField]) ? item[props.colorField] as ColorValue : DEFAULT_CHART_COLORS[indicators.length % DEFAULT_CHART_COLORS.length]);
const finalPercent = (isNumber(percent) && percent >= 0) ? percent : (value / totalValidValue * 100); const finalPercent = (isNumber(percent) && percent >= 0) ? percent : (value > 0 ? value / totalValidValue * 100 : 0);
const displayPercent = formatPercentToLocalizedNumerals(finalPercent, 2, '&lt;0.01'); const displayPercent = formatPercentToLocalizedNumerals(finalPercent, 2, '<0.01');
const displayValue = props.amountValue ? formatAmountToLocalizedNumeralsWithCurrency(value, props.defaultCurrency) : value.toString(); const displayValue = props.amountValue ? formatAmountToLocalizedNumeralsWithCurrency(value, props.defaultCurrency) : value.toString();
indicators.push({ indicators.push({
@@ -90,16 +88,19 @@ const radarData = computed<RadarChartData>(() => {
color: isDarkMode.value ? '#ccc' : '#333' color: isDarkMode.value ? '#ccc' : '#333'
}); });
values.push(value); values.push(value > 0 ? value : 0);
tooltip += '<div><span class="chart-pointer" style="background-color: ' + color + '"></span>'; tooltip += '<div><span class="chart-pointer" style="background-color: ' + color + '"></span>';
tooltip += `<span>${name}</span>`; tooltip += `<span>${name}</span>`;
if (props.showValue && props.showPercent) { const showValue = props.showValue;
const showPercent = props.showPercent && value > 0;
if (showValue && showPercent) {
tooltip += `<span class="ms-1" style="float: inline-end">(${displayPercent})</span><span class="ms-5" style="float: inline-end">${displayValue}</span>`; tooltip += `<span class="ms-1" style="float: inline-end">(${displayPercent})</span><span class="ms-5" style="float: inline-end">${displayValue}</span>`;
} else if (props.showValue && !props.showPercent) { } else if (showValue && !showPercent) {
tooltip += `<span class="ms-5" style="float: inline-end">${displayValue}</span>`; tooltip += `<span class="ms-5" style="float: inline-end">${displayValue}</span>`;
} else if (!props.showValue && props.showPercent) { } else if (!showValue && showPercent) {
tooltip += `<span class="ms-5" style="float: inline-end">${displayPercent}</span>`; tooltip += `<span class="ms-5" style="float: inline-end">${displayPercent}</span>`;
} }
+30 -20
View File
@@ -3,18 +3,19 @@
<svg class="pie-chart" :viewBox="`${-diameter} ${-diameter} ${diameter * 2} ${diameter * 2}`"> <svg class="pie-chart" :viewBox="`${-diameter} ${-diameter} ${diameter * 2} ${diameter * 2}`">
<circle class="pie-chart-background" cx="0" cy="0" :r="diameter"></circle> <circle class="pie-chart-background" cx="0" cy="0" :r="diameter"></circle>
<circle class="pie-chart-item" <template :key="idx" v-for="(item, idx) in validItems">
fill="transparent" <circle class="pie-chart-item"
cx="0" cy="0" fill="transparent"
:r="diameter / 2" cx="0" cy="0"
:stroke="item.color" :r="diameter / 2"
:stroke-width="diameter" :stroke="item.color"
:stroke-dasharray="getItemStrokeDash(item)" :stroke-width="diameter"
:stroke-dashoffset="getItemDashOffset(item, validItems, itemCommonDashOffset)" :stroke-dasharray="getItemStrokeDash(item)"
:key="idx" :stroke-dashoffset="getItemDashOffset(item, validItems, itemCommonDashOffset)"
v-for="(item, idx) in validItems" @click="switchSelectedIndex(idx)"
@click="switchSelectedIndex(idx)"> v-if="item.actualValue > 0 && item.paintPercent > minPaintPercent">
</circle> </circle>
</template>
<circle class="pie-chart-text-background" <circle class="pie-chart-text-background"
cx="0" cy="0" cx="0" cy="0"
@@ -44,7 +45,7 @@
</f7-link> </f7-link>
<div class="pie-chart-toolbox-info"> <div class="pie-chart-toolbox-info">
<p v-if="showPercent && selectedItem"> <p v-if="showPercent && selectedItem && selectedItem.actualValue >= 0">
<f7-chip class="chip-placeholder" outline v-if="skeleton"> <f7-chip class="chip-placeholder" outline v-if="skeleton">
<span class="skeleton-text">Percent</span> <span class="skeleton-text">Percent</span>
</f7-chip> </f7-chip>
@@ -53,7 +54,7 @@
:style="getColorStyle(selectedItem?.color, '--f7-chip-outline-border-color')" :style="getColorStyle(selectedItem?.color, '--f7-chip-outline-border-color')"
v-else-if="!skeleton"></f7-chip> v-else-if="!skeleton"></f7-chip>
</p> </p>
<p v-else-if="showPercent && (!validItems || !validItems.length)"> <p v-else-if="showPercent && (!validItems || !validItems.length || !selectedItem || selectedItem.actualValue < 0)">
<f7-chip outline text="---"></f7-chip> <f7-chip outline text="---"></f7-chip>
</p> </p>
<f7-link class="pie-chart-selected-item-info" :no-link-class="!enableClickItem" v-if="selectedItem" @click="clickItem(selectedItem)"> <f7-link class="pie-chart-selected-item-info" :no-link-class="!enableClickItem" v-if="selectedItem" @click="clickItem(selectedItem)">
@@ -99,6 +100,7 @@ const emit = defineEmits<{
const { tt } = useI18n(); const { tt } = useI18n();
const { selectedIndex, validItems } = usePieChartBase(props); const { selectedIndex, validItems } = usePieChartBase(props);
const minPaintPercent = 0.0001; // 0.01%
const diameter: number = 100; const diameter: number = 100;
const circumference: number = diameter * Math.PI; const circumference: number = diameter * Math.PI;
@@ -106,7 +108,9 @@ const totalValidValue = computed<number>(() => {
let totalValidValue = 0; let totalValidValue = 0;
for (const item of validItems.value) { for (const item of validItems.value) {
totalValidValue += item.value; if (item.actualValue > 0 && item.paintPercent > minPaintPercent) {
totalValidValue += item.value;
}
} }
return totalValidValue; return totalValidValue;
@@ -122,11 +126,17 @@ const itemCommonDashOffset = computed<number>(() => {
for (let i = 0; i < Math.min(selectedIndex.value + 1, validItems.value.length); i++) { for (let i = 0; i < Math.min(selectedIndex.value + 1, validItems.value.length); i++) {
const item = validItems.value[i] as CommonPieChartDataItem; const item = validItems.value[i] as CommonPieChartDataItem;
if (item.actualPercent > 0) { if (item.actualValue > 0 && item.paintPercent > minPaintPercent) {
if (i === selectedIndex.value) { if (i === selectedIndex.value) {
offset += -circumference * (1 - item.actualPercent) / 2; offset += -circumference * (1 - item.paintPercent) / 2;
} else { } else {
offset += -circumference * (1 - item.actualPercent); offset += -circumference * (1 - item.paintPercent);
}
} else {
if (i === selectedIndex.value) {
offset += -circumference / 2;
} else {
offset += -circumference;
} }
} }
} }
@@ -147,7 +157,7 @@ function getColorStyle(color: ColorStyleValue, additionalFieldName?: string): Re
} }
function getItemStrokeDash(item: CommonPieChartDataItem): string { function getItemStrokeDash(item: CommonPieChartDataItem): string {
const length = item.actualPercent * circumference; const length = item.paintPercent * circumference;
return `${length} ${circumference - length}`; return `${length} ${circumference - length}`;
} }
@@ -159,7 +169,7 @@ function getItemDashOffset(item: CommonPieChartDataItem, items: CommonPieChartDa
break; break;
} }
allPreviousPercent += curItem.actualPercent; allPreviousPercent += curItem.paintPercent > 0 ? curItem.paintPercent : 0;
} }
if (offset) { if (offset) {
@@ -117,7 +117,6 @@
/> />
<radar-chart <radar-chart
:items="categoryDimensionTransactionExplorerData && categoryDimensionTransactionExplorerData.length ? categoryDimensionTransactionExplorerData : []" :items="categoryDimensionTransactionExplorerData && categoryDimensionTransactionExplorerData.length ? categoryDimensionTransactionExplorerData : []"
:min-valid-percent="0.0001"
:show-value="true" :show-value="true"
:show-percent="true" :show-percent="true"
:amount-value="currentExplorer.valueMetric !== TransactionExplorerValueMetric.TransactionCount.value" :amount-value="currentExplorer.valueMetric !== TransactionExplorerValueMetric.TransactionCount.value"
@@ -263,7 +263,6 @@
/> />
<pie-chart <pie-chart
:items="categoricalAnalysisData && categoricalAnalysisData.items && categoricalAnalysisData.items.length ? categoricalAnalysisData.items : []" :items="categoricalAnalysisData && categoricalAnalysisData.items && categoricalAnalysisData.items.length ? categoricalAnalysisData.items : []"
:min-valid-percent="0.0001"
:show-value="showAmountInChart" :show-value="showAmountInChart"
:show-percent="showPercentInCategoricalChart" :show-percent="showPercentInCategoricalChart"
:enable-click-item="true" :enable-click-item="true"
@@ -316,7 +315,7 @@
<div class="d-flex flex-column ms-2"> <div class="d-flex flex-column ms-2">
<div class="d-flex"> <div class="d-flex">
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
<small class="statistics-percent" v-if="showPercentInCategoricalChart && item.percent >= 0">{{ formatPercentToLocalizedNumerals(item.percent, 2, '&lt;0.01') }}</small> <small class="statistics-percent" v-if="showPercentInCategoricalChart && item.percent >= 0 && item.totalAmount >= 0">{{ formatPercentToLocalizedNumerals(item.percent, 2, '<0.01') }}</small>
<v-spacer/> <v-spacer/>
<span class="statistics-amount">{{ getDisplayAmount(item.totalAmount, defaultCurrency) }}</span> <span class="statistics-amount">{{ getDisplayAmount(item.totalAmount, defaultCurrency) }}</span>
</div> </div>
@@ -351,7 +350,6 @@
/> />
<radar-chart <radar-chart
:items="categoricalAnalysisData && categoricalAnalysisData.items && categoricalAnalysisData.items.length ? categoricalAnalysisData.items : []" :items="categoricalAnalysisData && categoricalAnalysisData.items && categoricalAnalysisData.items.length ? categoricalAnalysisData.items : []"
:min-valid-percent="0.0001"
:show-value="showAmountInChart" :show-value="showAmountInChart"
:show-percent="showPercentInCategoricalChart" :show-percent="showPercentInCategoricalChart"
:amount-value="true" :amount-value="true"
@@ -80,7 +80,6 @@
></pie-chart> ></pie-chart>
<pie-chart <pie-chart
:items="categoricalAnalysisData.items" :items="categoricalAnalysisData.items"
:min-valid-percent="0.0001"
:show-value="showAmountInChart" :show-value="showAmountInChart"
:show-percent="showPercentInCategoricalChart" :show-percent="showPercentInCategoricalChart"
:show-center-text="true" :show-center-text="true"
@@ -183,7 +182,7 @@
<template #title> <template #title>
<div class="statistics-list-item-text"> <div class="statistics-list-item-text">
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
<small class="statistics-percent" v-if="showPercentInCategoricalChart && item.percent >= 0">{{ formatPercentToLocalizedNumerals(item.percent, 2, '&lt;0.01') }}</small> <small class="statistics-percent" v-if="showPercentInCategoricalChart && item.percent >= 0 && item.totalAmount >= 0">{{ formatPercentToLocalizedNumerals(item.percent, 2, '<0.01') }}</small>
</div> </div>
</template> </template>