migrate desktop pie chart to composition API and typescript
This commit is contained in:
+288
-268
@@ -3,298 +3,318 @@
|
|||||||
@click="clickItem" @legendselectchanged="onLegendSelectChanged" />
|
@click="clickItem" @legendselectchanged="onLegendSelectChanged" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, watch } from 'vue';
|
||||||
import { useTheme } from 'vuetify';
|
import { useTheme } from 'vuetify';
|
||||||
|
|
||||||
import { mapStores } from 'pinia';
|
import type { ECElementEvent } from 'echarts/core';
|
||||||
import { useSettingsStore } from '@/stores/setting.ts';
|
import type { CallbackDataParams } from 'echarts/types/dist/shared';
|
||||||
import { useUserStore } from '@/stores/user.ts';
|
|
||||||
|
|
||||||
import { DEFAULT_ICON_COLOR, DEFAULT_CHART_COLORS } from '@/consts/color.ts';
|
import { useI18n } from '@/locales/helpers.ts';
|
||||||
|
|
||||||
|
import type { ColorValue } from '@/core/color.ts';
|
||||||
import { ThemeType } from '@/core/theme.ts';
|
import { ThemeType } from '@/core/theme.ts';
|
||||||
|
import { DEFAULT_ICON_COLOR, DEFAULT_CHART_COLORS } from '@/consts/color.ts';
|
||||||
|
|
||||||
|
import { isNumber } from '@/lib/common.ts';
|
||||||
import { formatPercent } from '@/lib/numeral.ts';
|
import { formatPercent } from '@/lib/numeral.ts';
|
||||||
|
|
||||||
export default {
|
interface DesktopPieChartDataItem {
|
||||||
props: [
|
id: string;
|
||||||
'skeleton',
|
name: string;
|
||||||
'items',
|
displayName: string;
|
||||||
'idField',
|
value: number;
|
||||||
'nameField',
|
percent: number;
|
||||||
'valueField',
|
actualPercent: number;
|
||||||
'percentField',
|
itemStyle: {
|
||||||
'colorField',
|
color: ColorValue;
|
||||||
'hiddenField',
|
};
|
||||||
'minValidPercent',
|
selected: boolean;
|
||||||
'defaultCurrency',
|
sourceItem: Record<string, unknown>;
|
||||||
'showValue',
|
displayPercent?: string;
|
||||||
'enableClickItem'
|
displayValue?: string;
|
||||||
],
|
}
|
||||||
emits: [
|
|
||||||
'click'
|
|
||||||
],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
selectedLegends: null,
|
|
||||||
selectedIndex: 0
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapStores(useSettingsStore, useUserStore),
|
|
||||||
isDarkMode() {
|
|
||||||
return this.globalTheme.global.name.value === ThemeType.Dark;
|
|
||||||
},
|
|
||||||
itemsMap: function () {
|
|
||||||
const map = {};
|
|
||||||
|
|
||||||
for (let i = 0; i < this.items.length; i++) {
|
const props = defineProps<{
|
||||||
const item = this.items[i];
|
skeleton?: boolean;
|
||||||
let id = '';
|
items: Record<string, unknown>[];
|
||||||
|
idField?: string;
|
||||||
|
nameField: string;
|
||||||
|
valueField: string;
|
||||||
|
percentField?: string;
|
||||||
|
colorField?: string;
|
||||||
|
hiddenField?: string;
|
||||||
|
minValidPercent?: number;
|
||||||
|
defaultCurrency?: string;
|
||||||
|
showValue?: boolean;
|
||||||
|
enableClickItem?: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
if (this.idField && item[this.idField]) {
|
const emit = defineEmits<{
|
||||||
id = item[this.idField];
|
(e: 'click', value: Record<string, unknown>): void;
|
||||||
} else {
|
}>();
|
||||||
id = item[this.nameField];
|
|
||||||
}
|
|
||||||
|
|
||||||
map[id] = item;
|
const theme = useTheme();
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
const { formatAmountWithCurrency } = useI18n();
|
||||||
},
|
|
||||||
validItems: function () {
|
|
||||||
let totalValidValue = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < this.items.length; i++) {
|
const selectedLegends = ref<Record<string, boolean> | null>(null);
|
||||||
const item = this.items[i];
|
const selectedIndex = ref<number>(0);
|
||||||
|
|
||||||
if (item[this.valueField] && item[this.valueField] > 0 && (!this.hiddenField || !item[this.hiddenField])) {
|
const isDarkMode = computed<boolean>(() => theme.global.name.value === ThemeType.Dark);
|
||||||
totalValidValue += item[this.valueField];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const validItems = [];
|
const itemsMap = computed<Record<string, Record<string, unknown>>>(() => {
|
||||||
|
const map: Record<string, Record<string, unknown>> = {};
|
||||||
|
|
||||||
for (let i = 0; i < this.items.length; i++) {
|
for (let i = 0; i < props.items.length; i++) {
|
||||||
const item = this.items[i];
|
const item = props.items[i];
|
||||||
|
let id = '';
|
||||||
|
|
||||||
if (item[this.valueField] && item[this.valueField] > 0 &&
|
if (props.idField && item[props.idField]) {
|
||||||
(!this.hiddenField || !item[this.hiddenField]) &&
|
id = item[props.idField] as string;
|
||||||
(!this.minValidPercent || item[this.valueField] / totalValidValue > this.minValidPercent)) {
|
} else {
|
||||||
const finalItem = {
|
id = item[props.nameField] as string;;
|
||||||
id: (this.idField && item[this.idField]) ? item[this.idField] : item[this.nameField],
|
|
||||||
name: (this.idField && item[this.idField]) ? item[this.idField] : item[this.nameField],
|
|
||||||
displayName: item[this.nameField],
|
|
||||||
value: item[this.valueField],
|
|
||||||
percent: (item[this.percentField] > 0 || item[this.percentField] === 0 || item[this.percentField] === '0') ? item[this.percentField] : (item[this.valueField] / totalValidValue * 100),
|
|
||||||
actualPercent: item[this.valueField] / totalValidValue,
|
|
||||||
itemStyle: {
|
|
||||||
color: this.getColor(item[this.colorField] ? item[this.colorField] : DEFAULT_CHART_COLORS[validItems.length % DEFAULT_CHART_COLORS.length]),
|
|
||||||
},
|
|
||||||
selected: true,
|
|
||||||
sourceItem: item
|
|
||||||
};
|
|
||||||
|
|
||||||
finalItem.displayPercent = formatPercent(finalItem.percent, 2, '<0.01');
|
|
||||||
finalItem.displayValue = this.getDisplayCurrency(finalItem.value, this.defaultCurrency);
|
|
||||||
|
|
||||||
validItems.push(finalItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return validItems;
|
|
||||||
},
|
|
||||||
hasUnselectedItem: function () {
|
|
||||||
for (let i = 0; i < this.validItems.length; i++) {
|
|
||||||
const item = this.validItems[i];
|
|
||||||
|
|
||||||
if (this.selectedLegends && !this.selectedLegends[item.id]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
firstItemAndHalfCurrentItemTotalPercent: function () {
|
|
||||||
let totalValue = 0;
|
|
||||||
let firstValue = null;
|
|
||||||
let firstToCurrentTotalValue = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < this.validItems.length; i++) {
|
|
||||||
const item = this.validItems[i];
|
|
||||||
|
|
||||||
if (this.selectedLegends && !this.selectedLegends[item.id]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (firstValue === null) {
|
|
||||||
firstValue = item.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (firstValue !== null) {
|
|
||||||
if (i < this.selectedIndex) {
|
|
||||||
firstToCurrentTotalValue += item.value;
|
|
||||||
} else if (i === this.selectedIndex) {
|
|
||||||
firstToCurrentTotalValue += item.value / 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
totalValue += item.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (firstToCurrentTotalValue && totalValue > 0) {
|
|
||||||
return firstToCurrentTotalValue / totalValue;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
chartOptions: function () {
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
return {
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'item',
|
|
||||||
backgroundColor: self.isDarkMode ? '#333' : '#fff',
|
|
||||||
borderColor: self.isDarkMode ? '#333' : '#fff',
|
|
||||||
textStyle: {
|
|
||||||
color: self.isDarkMode ? '#eee' : '#333'
|
|
||||||
},
|
|
||||||
formatter: params => {
|
|
||||||
const name = params.data ? params.data.displayName : '';
|
|
||||||
const value = params.data ? params.data.displayValue : self.getDisplayCurrency(params.value);
|
|
||||||
let percent = params.data ? params.data.displayPercent : (params.percent + '%');
|
|
||||||
|
|
||||||
if (self.hasUnselectedItem) {
|
|
||||||
percent = params.percent + '%';
|
|
||||||
}
|
|
||||||
|
|
||||||
let tooltip = `<div><span class="chart-pointer" style="background-color: ${params.color}"></span>`;
|
|
||||||
|
|
||||||
if (name) {
|
|
||||||
tooltip += `<span>${name}</span><br/><span>${value} (${percent})</span>`;
|
|
||||||
} else {
|
|
||||||
tooltip += `<span>${value} (${percent})</span>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
tooltip += '</div>';
|
|
||||||
|
|
||||||
return tooltip;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
orient: 'horizontal',
|
|
||||||
data: self.validItems.map(item => item.name),
|
|
||||||
selected: self.selectedLegends,
|
|
||||||
textStyle: {
|
|
||||||
color: self.isDarkMode ? '#eee' : '#333'
|
|
||||||
},
|
|
||||||
formatter: id => {
|
|
||||||
return self.itemsMap[id] && self.nameField && self.itemsMap[id][self.nameField] ? self.itemsMap[id][self.nameField] : id;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
type: 'pie',
|
|
||||||
data: self.validItems,
|
|
||||||
top: 50,
|
|
||||||
startAngle: -90 + self.firstItemAndHalfCurrentItemTotalPercent * 360,
|
|
||||||
emphasis: {
|
|
||||||
itemStyle: {
|
|
||||||
shadowBlur: 10,
|
|
||||||
shadowOffsetX: 0,
|
|
||||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
color: self.isDarkMode ? '#eee' : '#333',
|
|
||||||
formatter: params => {
|
|
||||||
return params.data ? params.data.displayName : '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
animation: !self.skeleton
|
|
||||||
}
|
|
||||||
],
|
|
||||||
media: [
|
|
||||||
{
|
|
||||||
query: {
|
|
||||||
minWidth: 600
|
|
||||||
},
|
|
||||||
option: {
|
|
||||||
legend: {
|
|
||||||
orient: 'vertical',
|
|
||||||
left: 'left'
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
type: 'pie',
|
|
||||||
top: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'items': function () {
|
|
||||||
this.selectedIndex = 0;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setup() {
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return {
|
map[id] = item;
|
||||||
globalTheme: theme
|
}
|
||||||
};
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
clickItem: function (e) {
|
|
||||||
if (!this.enableClickItem || e.componentType !== 'series' || e.seriesType !=='pie') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.event && e.event.target && e.event.target.currentStates && e.event.target.currentStates[0] && e.event.target.currentStates[0] === 'emphasis') {
|
return map;
|
||||||
this.selectedIndex = e.dataIndex;
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!e.data || !e.data.sourceItem) {
|
const validItems = computed<DesktopPieChartDataItem[]>(() => {
|
||||||
return;
|
let totalValidValue = 0;
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit('click', e.data.sourceItem);
|
for (let i = 0; i < props.items.length; i++) {
|
||||||
},
|
const item = props.items[i];
|
||||||
onLegendSelectChanged: function (e) {
|
const value = item[props.valueField];
|
||||||
this.selectedLegends = e.selected;
|
|
||||||
const selectedItem = this.validItems[this.selectedIndex];
|
|
||||||
|
|
||||||
if (!selectedItem || !this.selectedLegends[selectedItem.id]) {
|
if (isNumber(value) && value > 0 && (!props.hiddenField || !item[props.hiddenField])) {
|
||||||
let newSelectedIndex = 0;
|
totalValidValue += value;
|
||||||
|
|
||||||
for (let i = 0; i < this.validItems.length; i++) {
|
|
||||||
const item = this.validItems[i];
|
|
||||||
|
|
||||||
if (this.selectedLegends[item.id]) {
|
|
||||||
newSelectedIndex = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.selectedIndex = newSelectedIndex;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getColor: function (color) {
|
|
||||||
if (color && color !== DEFAULT_ICON_COLOR) {
|
|
||||||
color = '#' + color;
|
|
||||||
}
|
|
||||||
|
|
||||||
return color;
|
|
||||||
},
|
|
||||||
getDisplayCurrency(value, currencyCode) {
|
|
||||||
return this.$locale.formatAmountWithCurrency(this.settingsStore, this.userStore, value, currencyCode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const validItems: DesktopPieChartDataItem[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < props.items.length; i++) {
|
||||||
|
const item = props.items[i];
|
||||||
|
const value = item[props.valueField];
|
||||||
|
const percent = props.percentField ? item[props.percentField] : -1;
|
||||||
|
|
||||||
|
if (isNumber(value) && value > 0 &&
|
||||||
|
(!props.hiddenField || !item[props.hiddenField]) &&
|
||||||
|
(!props.minValidPercent || value / totalValidValue > props.minValidPercent)) {
|
||||||
|
const finalItem: DesktopPieChartDataItem = {
|
||||||
|
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,
|
||||||
|
displayName: item[props.nameField] as string,
|
||||||
|
value: value,
|
||||||
|
percent: (isNumber(percent) && percent >= 0) ? percent : (value / totalValidValue * 100),
|
||||||
|
actualPercent: value / totalValidValue,
|
||||||
|
itemStyle: {
|
||||||
|
color: getColor((props.colorField && item[props.colorField]) ? item[props.colorField] as ColorValue : DEFAULT_CHART_COLORS[validItems.length % DEFAULT_CHART_COLORS.length]),
|
||||||
|
},
|
||||||
|
selected: true,
|
||||||
|
sourceItem: item
|
||||||
|
};
|
||||||
|
|
||||||
|
finalItem.displayPercent = formatPercent(finalItem.percent, 2, '<0.01');
|
||||||
|
finalItem.displayValue = formatAmountWithCurrency(finalItem.value, props.defaultCurrency) as string;
|
||||||
|
|
||||||
|
validItems.push(finalItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return validItems;
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasUnselectedItem = computed<boolean>(() => {
|
||||||
|
for (let i = 0; i < validItems.value.length; i++) {
|
||||||
|
const item = validItems.value[i];
|
||||||
|
|
||||||
|
if (selectedLegends.value && !selectedLegends.value[item.id]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const firstItemAndHalfCurrentItemTotalPercent = computed<number>(() => {
|
||||||
|
let totalValue = 0;
|
||||||
|
let firstValue = null;
|
||||||
|
let firstToCurrentTotalValue = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < validItems.value.length; i++) {
|
||||||
|
const item = validItems.value[i];
|
||||||
|
|
||||||
|
if (selectedLegends.value && !selectedLegends.value[item.id]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstValue === null) {
|
||||||
|
firstValue = item.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstValue !== null) {
|
||||||
|
if (i < selectedIndex.value) {
|
||||||
|
firstToCurrentTotalValue += item.value;
|
||||||
|
} else if (i === selectedIndex.value) {
|
||||||
|
firstToCurrentTotalValue += item.value / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
totalValue += item.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstToCurrentTotalValue && totalValue > 0) {
|
||||||
|
return firstToCurrentTotalValue / totalValue;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const chartOptions = computed(() => {
|
||||||
|
return {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
backgroundColor: isDarkMode.value ? '#333' : '#fff',
|
||||||
|
borderColor: isDarkMode.value ? '#333' : '#fff',
|
||||||
|
textStyle: {
|
||||||
|
color: isDarkMode.value ? '#eee' : '#333'
|
||||||
|
},
|
||||||
|
formatter: (params: CallbackDataParams) => {
|
||||||
|
const dataItem = params.data as DesktopPieChartDataItem;
|
||||||
|
const name = dataItem ? dataItem.displayName : '';
|
||||||
|
const value = dataItem ? dataItem.displayValue : formatAmountWithCurrency(params.value as number);
|
||||||
|
let percent = dataItem ? dataItem.displayPercent : (params.percent + '%');
|
||||||
|
|
||||||
|
if (hasUnselectedItem.value) {
|
||||||
|
percent = params.percent + '%';
|
||||||
|
}
|
||||||
|
|
||||||
|
let tooltip = `<div><span class="chart-pointer" style="background-color: ${params.color}"></span>`;
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
tooltip += `<span>${name}</span><br/><span>${value} (${percent})</span>`;
|
||||||
|
} else {
|
||||||
|
tooltip += `<span>${value} (${percent})</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
tooltip += '</div>';
|
||||||
|
|
||||||
|
return tooltip;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'horizontal',
|
||||||
|
data: validItems.value.map(item => item.name),
|
||||||
|
selected: selectedLegends.value,
|
||||||
|
textStyle: {
|
||||||
|
color: isDarkMode.value ? '#eee' : '#333'
|
||||||
|
},
|
||||||
|
formatter: (id: string) => {
|
||||||
|
const item = itemsMap.value[id];
|
||||||
|
return item && props.nameField && item[props.nameField] ? item[props.nameField] as string : id;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
data: validItems.value,
|
||||||
|
top: 50,
|
||||||
|
startAngle: -90 + firstItemAndHalfCurrentItemTotalPercent.value * 360,
|
||||||
|
emphasis: {
|
||||||
|
itemStyle: {
|
||||||
|
shadowBlur: 10,
|
||||||
|
shadowOffsetX: 0,
|
||||||
|
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
color: isDarkMode.value ? '#eee' : '#333',
|
||||||
|
formatter: (params: CallbackDataParams) => {
|
||||||
|
const dataItem = params.data as DesktopPieChartDataItem;
|
||||||
|
return dataItem ? dataItem.displayName : '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
animation: !props.skeleton
|
||||||
|
}
|
||||||
|
],
|
||||||
|
media: [
|
||||||
|
{
|
||||||
|
query: {
|
||||||
|
minWidth: 600,
|
||||||
|
},
|
||||||
|
option: {
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
left: 'left'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
top: 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function getColor(color: string): ColorValue {
|
||||||
|
if (color && color !== DEFAULT_ICON_COLOR) {
|
||||||
|
color = '#' + color;
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clickItem(e: ECElementEvent): void {
|
||||||
|
if (!props.enableClickItem || e.componentType !== 'series' || e.seriesType !=='pie') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.event && e.event.target && e.event.target.currentStates && e.event.target.currentStates[0] && e.event.target.currentStates[0] === 'emphasis') {
|
||||||
|
selectedIndex.value = e.dataIndex;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = e.data as object;
|
||||||
|
|
||||||
|
if ('sourceItem' in data) {
|
||||||
|
emit('click', data.sourceItem as Record<string, unknown>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onLegendSelectChanged(e: { selected: Record<string, boolean> }): void {
|
||||||
|
selectedLegends.value = e.selected;
|
||||||
|
const selectedItem = validItems.value[selectedIndex.value];
|
||||||
|
|
||||||
|
if (!selectedItem || !selectedLegends.value[selectedItem.id]) {
|
||||||
|
let newSelectedIndex = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < validItems.value.length; i++) {
|
||||||
|
const item = validItems.value[i];
|
||||||
|
|
||||||
|
if (selectedLegends.value[item.id]) {
|
||||||
|
newSelectedIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedIndex.value = newSelectedIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.items, () => {
|
||||||
|
selectedIndex.value = 0;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
Reference in New Issue
Block a user