| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- <template>
- <view class="kpi-grid">
- <view class="kpi-card bg-white radius" v-for="(item, index) in list" :key="index">
- <view class="kpi-card__header flex">
- <view class="kpi-card__icon" :style="{ backgroundColor: `${item.color}18` }">
- <text class="kpi-card__icon-text" :style="{ color: item.color }">{{ item.icon }}</text>
- </view>
- <text class="kpi-card__title">{{ item.title }}</text>
- </view>
- <view class="kpi-card__value">
- <text class="kpi-card__num">{{ item.value }}</text>
- <text class="kpi-card__unit">{{ item.unit }}</text>
- </view>
- <view class="kpi-card__compare flex" v-if="item.showCompare">
- <view class="kpi-card__compare-item">
- <text class="kpi-card__compare-label">环比</text>
- <text :class="item.mom >= 0 ? 'kpi-card__up' : 'kpi-card__down'">{{ formatRate(item.mom) }}</text>
- </view>
- <view class="kpi-card__compare-item">
- <text class="kpi-card__compare-label">同比</text>
- <text :class="item.yoy >= 0 ? 'kpi-card__up' : 'kpi-card__down'">{{ formatRate(item.yoy) }}</text>
- </view>
- </view>
- </view>
- </view>
- </template>
- <script setup>
- import { reactive, computed, watch, toRefs } from "vue";
- import { getEmsClassificationEnergy, getEmsOverviewTop } from "@/api/business/ems/overview.js";
- const PIE_COLORS = ["#4fe3c3", "#6797ed", "#9978fa", "#4ecee2", "#ffbb62", "#ff7d2e", "#dc76ee", "#56d853"];
- const emit = defineEmits(["update:unitIndex", "update:categoryData"]);
- const props = defineProps({
- dateType: {
- type: String,
- default: "1",
- },
- energyType: {
- type: String,
- default: "1",
- },
- unitIndex: {
- type: Number,
- default: 0,
- },
- categoryData: {
- type: Array,
- default: () => [],
- },
- });
- const DEFAULT_KPI = [
- { title: "能耗用量", value: "0.0", unit: "kWh", icon: "⚡", color: "#4a90e2", showCompare: true, mom: 0, yoy: 0 },
- { title: "等值树", value: "0.0", unit: "棵", icon: "🌳", color: "#40b883", showCompare: true, mom: 0, yoy: 0 },
- { title: "标准煤", value: "0.0", unit: "kgce", icon: "◉", color: "#f5a623", showCompare: true, mom: 0, yoy: 0 },
- { title: "碳排放", value: "0.0", unit: "kgce", icon: "☁", color: "#7b61ff", showCompare: true, mom: 0, yoy: 0 },
- { title: "综合能耗统计", value: "0.0", unit: "kgce", icon: "◎", color: "#9b59b6", showCompare: false, mom: 0, yoy: 0 },
- { title: "折算碳减排量", value: "0.0", unit: "kg", icon: "♻", color: "#ef6b6b", showCompare: false, mom: 0, yoy: 0 },
- ];
- const state = reactive({
- list: DEFAULT_KPI.map((item) => ({ ...item })),
- });
- const { list } = toRefs(state);
- const usageUnit = computed(() => (String(props.energyType) === "1" ? "kWh" : "m³"));
- function formatRate(value) {
- const num = Number(value) || 0;
- return `${num >= 0 ? "+" : ""}${num.toFixed(1)}%`;
- }
- function toFixedNum(val, fallback = 0) {
- const num = Number(val);
- return Number.isFinite(num) ? Number(num.toFixed(1)) : fallback;
- }
- function getQueryParams() {
- return {
- dateType: props.dateType,
- energyType: props.energyType,
- };
- }
- function applyClassificationEnergy(data) {
- if (!data) return;
- state.list[0].value = toFixedNum(data.consume);
- state.list[0].unit = usageUnit.value;
- state.list[0].mom = toFixedNum(data.sequentialCon);
- state.list[0].yoy = toFixedNum(data.pariPassCon);
- state.list[1].value = toFixedNum(data.plantTree);
- state.list[1].mom = toFixedNum(data.sequentialPlantTree);
- state.list[1].yoy = toFixedNum(data.pariPassPlantTree);
- state.list[2].value = toFixedNum(data.coalAmount);
- state.list[2].mom = toFixedNum(data.sequentialCoal);
- state.list[2].yoy = toFixedNum(data.pariPassCoal);
- state.list[3].value = toFixedNum(data.co2Amount);
- state.list[3].mom = toFixedNum(data.sequentialCo2);
- state.list[3].yoy = toFixedNum(data.pariPassCo2);
- }
- function applyOverviewTopKpi(data) {
- if (!data) return;
- state.list[4].value = toFixedNum(data.coalTotal);
- state.list[5].value = toFixedNum(data.co2Total);
- emit("update:unitIndex", toFixedNum(data.unitCoal));
- if (Array.isArray(data.ratioList) && data.ratioList.length) {
- emit(
- "update:categoryData",
- data.ratioList.map((item, index) => ({
- name: item.name,
- value: toFixedNum(item.consume),
- color: PIE_COLORS[index % PIE_COLORS.length],
- }))
- );
- }
- }
- function fetchClassificationEnergy() {
- return getEmsClassificationEnergy(getQueryParams())
- .then((requset) => {
- if (requset.status === "SUCCESS") {
- applyClassificationEnergy(requset.data);
- }
- })
- .catch(() => {});
- }
- function fetchOverviewTop() {
- return getEmsOverviewTop({ dateType: props.dateType })
- .then((requset) => {
- if (requset.status === "SUCCESS" && requset.data) {
- applyOverviewTopKpi(requset.data);
- }
- })
- .catch(() => {});
- }
- function refresh() {
- return Promise.all([fetchClassificationEnergy(), fetchOverviewTop()]);
- }
- watch(
- () => [props.dateType, props.energyType],
- () => {
- refresh();
- },
- { immediate: true }
- );
- defineExpose({
- refresh,
- });
- </script>
- <style lang="scss" scoped>
- .kpi-grid {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 10px;
- margin-bottom: 10px;
- }
- .kpi-card {
- padding: 12px;
- &__header {
- align-items: center;
- margin-bottom: 8px;
- }
- &__icon {
- width: 28px;
- height: 28px;
- border-radius: 8px;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-right: 6px;
- flex-shrink: 0;
- }
- &__icon-text {
- font-size: 14px;
- line-height: 1;
- }
- &__title {
- font-size: 12px;
- color: #666;
- line-height: 18px;
- }
- &__value {
- margin-bottom: 6px;
- }
- &__num {
- font-size: 20px;
- font-weight: 600;
- color: #333;
- }
- &__unit {
- margin-left: 4px;
- font-size: 11px;
- color: #999;
- }
- &__compare {
- gap: 12px;
- }
- &__compare-item {
- font-size: 10px;
- color: #999;
- }
- &__compare-label {
- margin-right: 4px;
- }
- &__up {
- color: #ef4444;
- }
- &__down {
- color: #16a34a;
- }
- }
- </style>
|