|
@@ -0,0 +1,114 @@
|
|
|
+<template>
|
|
|
+ <div class="metrics-cards">
|
|
|
+ <MCard
|
|
|
+ v-model="info.metric"
|
|
|
+ :metric-items="allMetricItems"
|
|
|
+ :color="info.color"
|
|
|
+ v-for="info in displayMetrics"
|
|
|
+ @change-metric="changedMetric"
|
|
|
+ @click="clickCard(info.metric)"/>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script lang="ts" setup>
|
|
|
+import { ref, withDefaults, Ref, onBeforeMount, watch, computed,ComputedRef } from 'vue'
|
|
|
+import MCard from './mCard.vue'
|
|
|
+
|
|
|
+interface ModelData {
|
|
|
+ metric: string,
|
|
|
+ color: string
|
|
|
+}
|
|
|
+interface Props {
|
|
|
+ modelValue: ModelData[],
|
|
|
+ metricItems: MetricData[],
|
|
|
+ colors?: string[]
|
|
|
+}
|
|
|
+const colorsMap: { [key: string]: boolean } = {}
|
|
|
+const props = withDefaults(defineProps<Props>(), { colors: () => ["aqua", "orange", "blue"] })
|
|
|
+const emits = defineEmits(['change', 'update:modelValue'])
|
|
|
+const allMetricItems = ref(props.metricItems)
|
|
|
+const selectedMetric = ref(props.modelValue)
|
|
|
+const displayMetrics: Ref<{metric:string, color?: string}[]> = ref([])
|
|
|
+
|
|
|
+onBeforeMount(()=> {
|
|
|
+ for (const color of props.colors) {
|
|
|
+ colorsMap[color] = false
|
|
|
+ }
|
|
|
+ const tmp:{[key: string]: boolean} = {}
|
|
|
+ for (const info of selectedMetric.value) {
|
|
|
+ displayMetrics.value.push({ metric: info.metric, color: info.color })
|
|
|
+ tmp[info.metric] = true
|
|
|
+ }
|
|
|
+ for (const info of allMetricItems.value) {
|
|
|
+ if (info.disabled && !tmp[info.value]) { displayMetrics.value.push({ metric: info.value }) }
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const getColor = () => {
|
|
|
+ for (const [k,v] of Object.entries(colorsMap)) {
|
|
|
+ if (!v) return k
|
|
|
+ }
|
|
|
+ return ""
|
|
|
+}
|
|
|
+const changedMetric = (oldVal: string, newVal: string) => {
|
|
|
+ for (const info of allMetricItems.value) {
|
|
|
+ if (info.value === newVal) {
|
|
|
+ info.disabled = true
|
|
|
+ } else if (info.value === oldVal) {
|
|
|
+ info.disabled = false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const index = selectedMetric.value.findIndex( info => info.metric === oldVal)
|
|
|
+ if (index > -1) {
|
|
|
+ selectedMetric.value[index].metric = newVal
|
|
|
+ emits('update:modelValue', selectedMetric.value)
|
|
|
+ emits('change', selectedMetric.value)
|
|
|
+ }
|
|
|
+}
|
|
|
+const clickCard = (metric: string) => {
|
|
|
+ const index = selectedMetric.value.findIndex( info => info.metric === metric)
|
|
|
+ if (index > -1) { // 已存在则删除
|
|
|
+ if (selectedMetric.value.length <= 1 ) return
|
|
|
+ const tmp = selectedMetric.value[index]
|
|
|
+ selectedMetric.value.splice(index, 1)
|
|
|
+ colorsMap[tmp.color] = false
|
|
|
+ emits('update:modelValue', selectedMetric.value)
|
|
|
+ emits('change', selectedMetric.value)
|
|
|
+ } else { // 不存在则添加
|
|
|
+ if (selectedMetric.value.length === 3) {
|
|
|
+ selectedMetric.value[2].metric = metric
|
|
|
+ } else {
|
|
|
+ const color = getColor()
|
|
|
+ colorsMap[color] = true
|
|
|
+ selectedMetric.value.push({ metric: metric, color: color})
|
|
|
+ }
|
|
|
+ emits('update:modelValue', selectedMetric.value)
|
|
|
+ emits('change', selectedMetric.value)
|
|
|
+ }
|
|
|
+}
|
|
|
+watch(selectedMetric.value, () => {
|
|
|
+ const cache:{ [key: string]: string } = {}
|
|
|
+ for (const info of selectedMetric.value) {
|
|
|
+ cache[info.metric] = info.color
|
|
|
+ }
|
|
|
+ for (const info of displayMetrics.value) {
|
|
|
+ const color = cache[info.metric]
|
|
|
+ if (color) {
|
|
|
+ info.color = color
|
|
|
+ } else {
|
|
|
+ info.color = undefined
|
|
|
+ }
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.metrics-cards {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: flex-start;
|
|
|
+ gap: 12px;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+</style>
|