024369b323338e10d39cce09befab18087675139..8d3e9c9480ebc4ae95bdb5854de80d068c467343
2026-09-29 wangrong
Merge branch 'fix-home-logo'
8d3e9c 对比 | 目录
2026-09-29 wangrong
修改首页加logo等
e2d1d7 对比 | 目录
2026-09-26 wangrong
add screen-chart.ts
3c7f5d 对比 | 目录
2026-09-26 wangrong
修改首页显示和统计任务
781539 对比 | 目录
10个文件已修改
2个文件已添加
312 ■■■■■ 已修改文件
.env 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/bak.png 补丁 | 查看 | 原始文档 | blame | 历史
public/logo.png 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/maps/hooks/use-map.ts 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/operation/views/wastesale.vue 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/report/components/screen-map-panel.vue 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/report/data/screen-chart.ts 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/report/views/screen/carbon.vue 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/report/views/screen/map.vue 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/report/views/screen/overview.vue 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/report/views/screen/participation.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/report/views/screen/resource.vue 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.env
@@ -1,5 +1,5 @@
# 应用名称
VITE_NAME = " WOMP "
VITE_NAME = " 无废校园 "
# 网络超时请求时间
VITE_TIMEOUT = 30000
public/bak.png
public/logo.png

src/modules/maps/hooks/use-map.ts
@@ -4,6 +4,52 @@
export const MAP_TK = '51915011aa0997aa2ddd7e38504fa8bc';
export const MAP_LOAD = { v: '4.0', tk: MAP_TK };
export const DEFAULT_CENTER: LngLat = [121.280637, 32.125178];
export const DEFAULT_ZOOM = 16;
export const MAP_ZOOM_KEY = 'map.zoomlevel';
export const MAP_COORD_KEY = 'map.coordinates';
function paramText(raw: unknown): string {
    if (raw == null) return '';
    if (typeof raw === 'string' || typeof raw === 'number') return String(raw).trim();
    if (Array.isArray(raw)) return raw.map(e => String(e).trim()).join(',');
    return '';
}
export function parseMapZoom(raw: unknown, fallback = DEFAULT_ZOOM) {
    const n = Number(paramText(raw));
    return Number.isFinite(n) && n > 0 ? n : fallback;
}
export function parseMapCenter(raw: unknown, fallback: LngLat = DEFAULT_CENTER): LngLat {
    const text = paramText(raw);
    const parts = text.split(/[,,]/).map(s => Number(s.trim()));
    if (parts.length >= 2 && Number.isFinite(parts[0]) && Number.isFinite(parts[1])) {
        return [parts[0], parts[1]];
    }
    return fallback;
}
/** 从 base_sys_param 读取学校地图默认视角:map.zoomlevel、map.coordinates(lon,lat) */
export async function loadMapView(service: any): Promise<{ center: LngLat; zoom: number }> {
    const read = async (key: string) => {
        try {
            if (service?.base?.sys?.param?.stringbykey) {
                return await service.base.sys.param.stringbykey({ key });
            }
            return await service.request({
                url: 'admin/base/sys/param/stringbykey',
                params: { key }
            });
        } catch {
            return '';
        }
    };
    const [zoomRaw, coordRaw] = await Promise.all([read(MAP_ZOOM_KEY), read(MAP_COORD_KEY)]);
    return {
        center: parseMapCenter(coordRaw),
        zoom: parseMapZoom(zoomRaw)
    };
}
export function pinIcon(color: string) {
    const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="28" height="40" viewBox="0 0 28 40"><path fill="${color}" stroke="#fff" stroke-width="1.5" d="M14 1C7.4 1 2 6.4 2 13c0 8.8 12 25 12 25s12-16.2 12-25C26 6.4 20.6 1 14 1z"/><circle cx="14" cy="13" r="5" fill="#fff"/></svg>`;
src/modules/operation/views/wastesale.vue
@@ -139,7 +139,7 @@
            required: true
        },
        {
            label: t('重量'),
            label: t('重量(kg)'),
            prop: "weight",
            hook: "number",
            component: {
@@ -233,7 +233,7 @@
            sortable: "custom",
            component: { name: "cl-date-text" }
        },
        { label: t('重量'), prop: "weight", minWidth: 140, sortable: "custom" },
        { label: t('重量(kg)'), prop: "weight", minWidth: 140, sortable: "custom" },
        { label: t('单价'), prop: "unitPrice", minWidth: 140, sortable: "custom" },
        { label: t('总价'), prop: "totalPrice", minWidth: 140, sortable: "custom" },
        {
src/modules/report/components/screen-map-panel.vue
@@ -11,9 +11,16 @@
            </div>
        </div>
        <!-- 地图主体 -->
        <!-- 地图主体:等参数就绪再创建,避免天地图只在初始化时吃 center/zoom -->
        <div class="map-panel__map">
            <tdt-map :center="center" :zoom="zoom" :controls="controls" :loadConfig="MAP_LOAD">
            <tdt-map
                v-if="viewReady"
                :center="center"
                :zoom="zoom"
                :controls="controls"
                :loadConfig="MAP_LOAD"
                @init="onMapInit"
            >
                <tdt-marker
                    v-for="item in list"
                    :key="keyOf(item)"
@@ -40,12 +47,15 @@
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { TdtMap, TdtMarker, type LngLat } from 'vue-tianditu2';
import { useCool } from '/@/cool';
import {
    DEFAULT_CENTER,
    DEFAULT_ZOOM,
    MAP_LOAD,
    SORTER_ICON,
    VEHICLE_ICON,
    formatTime,
    loadMapView,
    siteIcon,
    useMapsApi
} from '../../maps/hooks/use-map';
@@ -97,12 +107,14 @@
    }
};
const { service } = useCool();
const api = useMapsApi();
const layer = ref<LayerKey>('site');
const list = ref<any[]>([]);
const center = ref<LngLat>(DEFAULT_CENTER);
const zoom = ref(12);
const zoom = ref(DEFAULT_ZOOM);
const viewReady = ref(false);
const lastUpdate = ref('');
const controls = ['Zoom', 'Scale'];
@@ -131,14 +143,29 @@
    return cfg.value.icon(item);
}
function applyView(map: any) {
    const [lon, lat] = center.value;
    if (!map || !Number.isFinite(lon) || !Number.isFinite(lat)) return;
    const TMap = (window as any).T;
    const lnglat = TMap?.LngLat ? new TMap.LngLat(lon, lat) : [lon, lat];
    if (typeof map.centerAndZoom === 'function') {
        map.centerAndZoom(lnglat, zoom.value);
    } else {
        map.panTo?.(lnglat);
        map.setZoom?.(zoom.value);
    }
    map.checkResize?.();
}
function onMapInit(map: any) {
    applyView(map);
    window.setTimeout(() => applyView(map), 200);
}
async function load() {
    const res = await cfg.value.load();
    list.value = Array.isArray(res) ? res : [];
    lastUpdate.value = formatTime(new Date());
    if (list.value[0]) {
        center.value = pos(list.value[0]);
        zoom.value = 13;
    }
}
// 车辆 / 分拣员为实时位置,30s 轮询;站点是静态台账,不轮询
@@ -162,6 +189,10 @@
});
onMounted(async () => {
    const view = await loadMapView(service);
    center.value = view.center;
    zoom.value = view.zoom;
    viewReady.value = true;
    await load();
    startPolling();
});
src/modules/report/data/screen-chart.ts
New file
@@ -0,0 +1,55 @@
export type ChartRow<T> = { item: T; value: number; even: boolean };
/** 图表取值:0 不画;碳减排可为负,展示时取绝对值。 */
export function chartValue(raw: number, carbon = false) {
    const n = Number(raw);
    if (!Number.isFinite(n) || n === 0) return 0;
    return carbon ? Math.abs(n) : n;
}
/**
 * 分类图数据:
 * - 部分有值:只保留当前指标 > 0 的类
 * - 全部为 0:evenSplit 时均分扇区(value=1 仅用于几何,even 标记给 tooltip 显示 0)
 */
export function chartSeries<T>(
    items: T[] | undefined,
    pick: (item: T) => number,
    opts?: { carbon?: boolean; evenSplit?: boolean }
): ChartRow<T>[] {
    const list = items || [];
    const rows = list.map(item => ({
        item,
        raw: chartValue(pick(item), opts?.carbon)
    }));
    const positive = rows.filter(e => e.raw > 0);
    if (positive.length) {
        return positive.map(e => ({ item: e.item, value: e.raw, even: false }));
    }
    if (opts?.evenSplit === false) {
        return rows.map(e => ({ item: e.item, value: 0, even: false }));
    }
    return rows.map(e => ({ item: e.item, value: 1, even: true }));
}
export function pieTooltipFormatter(unit = '') {
    return (p: any) => {
        const val = p.data?.even ? 0 : p.value;
        const u = unit ? ` ${unit}` : '';
        const pct = p.percent != null ? ` (${p.percent}%)` : '';
        return `${p.name}<br/>${val}${u}${pct}`;
    };
}
export function toPieData<T>(
    rows: ChartRow<T>[],
    name: (item: T) => string,
    color: (item: T) => string | undefined
) {
    return rows.map(row => ({
        name: name(row.item),
        value: row.value,
        even: row.even,
        itemStyle: { color: color(row.item) }
    }));
}
src/modules/report/views/screen/carbon.vue
@@ -1,6 +1,6 @@
<template>
    <div class="screen-page">
        <div class="kpi-grid" style="--kpi-cols: 5">
        <div class="kpi-grid" style="--kpi-cols: 3">
            <kpi-card v-for="item in kpis" :key="item.label" v-bind="item" />
        </div>
@@ -46,6 +46,7 @@
import KpiCard from '../../components/kpi-card.vue';
import PanelCard from '../../components/panel-card.vue';
import { TIME_RANGE_OPTIONS } from '../../data/screen';
import { chartSeries, pieTooltipFormatter, toPieData } from '../../data/screen-chart';
import { useChartTheme, useScreenData, useScreenFieldSource } from '../../hooks/use-screen';
const { data, timeRange } = useScreenData();
@@ -57,11 +58,9 @@
);
const kpis = computed(() => [
    { field: 'kpis.totalCarbonTon', label: '累计碳减排', value: data.value.kpis.totalCarbonTon, suffix: '吨CO₂e', fixed: 1 },
    { field: 'kpis.periodCarbonTon', label: `${rangeLabel.value}碳减排`, value: data.value.kpis.periodCarbonTon, suffix: '吨CO₂e', fixed: 2 },
    { field: 'kpis.totalWeightTon', label: '累计资源回收', value: data.value.kpis.totalWeightTon, suffix: '吨', fixed: 1 },
    { field: 'kpis.totalParticipants', label: '累计参与师生', value: data.value.kpis.totalParticipants, suffix: '人', fixed: 0 },
    { field: 'kpis.greenActions', label: '累计环保行为', value: data.value.kpis.greenActions, suffix: '次', fixed: 0 }
    { field: 'kpis.periodWeightTon', label: `${rangeLabel.value}资源回收`, value: data.value.kpis.periodWeightTon, suffix: '吨', fixed: 2, hint: '当前时间范围' },
    { field: 'kpis.newParticipants', label: `${rangeLabel.value}参与师生`, value: data.value.kpis.newParticipants, suffix: '人', fixed: 0, hint: '区间内出现过的去重用户' },
    { field: 'kpis.greenActions', label: `${rangeLabel.value}环保行为`, value: data.value.kpis.greenActions, suffix: '次', fixed: 0, hint: '区间内已完成订单数' }
]);
const carbonTrendDemo = computed(() =>
@@ -80,7 +79,7 @@
        },
        yAxis: {
            type: 'value',
            name: '吨CO₂e',
            name: 'tCO₂e',
            nameTextStyle: { color: mutedColor.value },
            splitLine: { lineStyle: { color: splitColor.value, type: 'dashed' } },
            axisLabel: { color: mutedColor.value },
@@ -111,7 +110,7 @@
    },
    yAxis: {
        type: 'value',
        name: '吨CO₂e',
        name: 'tCO₂e',
        nameTextStyle: { color: mutedColor.value },
        splitLine: { lineStyle: { color: splitColor.value, type: 'dashed' } },
        axisLabel: { color: mutedColor.value },
@@ -129,7 +128,7 @@
}));
const sourceOption = computed(() => ({
    tooltip: { trigger: 'item', formatter: '{b}<br/>{c} kgCO₂e ({d}%)', ...tooltip() },
    tooltip: { trigger: 'item', ...tooltip(), formatter: pieTooltipFormatter('kgCO₂e') },
    legend: { bottom: 0, textStyle: { color: mutedColor.value } },
    series: [
        {
@@ -139,11 +138,11 @@
            padAngle: 3,
            itemStyle: { borderRadius: 6 },
            label: { color: textColor.value, formatter: '{b}\n{d}%' },
            data: data.value.recycleItems.map(e => ({
                name: e.name + '回收',
                value: e.carbon,
                itemStyle: { color: e.color }
            }))
            data: toPieData(
                chartSeries(data.value.recycleItems, e => e.carbon, { carbon: true }),
                e => e.name + '回收',
                e => e.color
            )
        }
    ]
}));
src/modules/report/views/screen/map.vue
@@ -85,6 +85,7 @@
import ScreenMapPanel from '../../components/screen-map-panel.vue';
import CameraMonitor from '../../../monitor/views/cameras.vue';
import { TIME_RANGE_OPTIONS } from '../../data/screen';
import { chartSeries, pieTooltipFormatter, toPieData } from '../../data/screen-chart';
import { useChartTheme, useScreenData, useScreenFieldSource } from '../../hooks/use-screen';
const { data, timeRange } = useScreenData();
@@ -96,21 +97,21 @@
const itemMetric = ref<'weight' | 'value' | 'carbon'>('weight');
const mixOptions = [
    { label: '重量', value: 'weight' },
    { label: '价值', value: 'value' },
    { label: '数量', value: 'count' }
    { label: '重量(kg)', value: 'weight' },
    { label: '价值(元)', value: 'value' },
    { label: '计数(次)', value: 'count' }
];
const trendOptions = [
    { label: '重量', value: 'weight' },
    { label: '价值', value: 'value' },
    { label: '碳减排', value: 'carbon' }
    { label: '重量(kg)', value: 'weight' },
    { label: '价值(元)', value: 'value' },
    { label: '碳减排(kgCO₂e)', value: 'carbon' }
];
const itemOptions = [
    { label: '重量', value: 'weight' },
    { label: '价值', value: 'value' },
    { label: '碳减排', value: 'carbon' }
    { label: '重量(kg)', value: 'weight' },
    { label: '价值(元)', value: 'value' },
    { label: '碳减排(kgCO₂e)', value: 'carbon' }
];
const rangeLabel = computed(
@@ -122,7 +123,7 @@
    { field: 'kpis.periodWeightTon', label: `${rangeLabel.value}回收量`, value: data.value.kpis.periodWeightTon, suffix: '吨', fixed: 2 },
    { field: 'kpis.resourceRate', label: '资源化率', value: data.value.kpis.resourceRate, suffix: '%', fixed: 1 },
    { field: 'kpis.periodValue', label: '回收价值', value: data.value.kpis.periodValue, suffix: '元', fixed: 0 },
    { field: 'kpis.periodCarbonTon', label: '碳减排', value: data.value.kpis.periodCarbonTon, suffix: '吨CO₂e', fixed: 2 },
    { field: 'kpis.periodCarbonTon', label: '碳减排(tCO₂e)', value: data.value.kpis.periodCarbonTon, suffix: 'tCO₂e', fixed: 2 },
    { field: 'kpis.totalWeightTon', label: '累计回收量', value: data.value.kpis.totalWeightTon, suffix: '吨', fixed: 1 },
    { field: 'kpis.totalParticipants', label: '累计参与人数', value: data.value.kpis.totalParticipants, suffix: '人', fixed: 0 },
    { field: 'kpis.totalPoints', label: '累计碳积分', value: data.value.kpis.totalPoints, suffix: '分', fixed: 0 }
@@ -142,7 +143,7 @@
/** 垃圾分类结构(饼) */
const mixOption = computed(() => ({
    tooltip: { trigger: 'item', formatter: '{b}<br/>{c} ({d}%)', ...tooltip() },
    tooltip: { trigger: 'item', ...tooltip(), formatter: pieTooltipFormatter() },
    series: [
        {
            type: 'pie',
@@ -151,18 +152,18 @@
            padAngle: 3,
            itemStyle: { borderRadius: 6 },
            label: { color: textColor.value, formatter: '{b}\n{d}%' },
            data: data.value.wasteMix.map(e => ({
                name: e.name,
                value: e[mixMetric.value],
                itemStyle: { color: e.color }
            }))
            data: toPieData(
                chartSeries(data.value.wasteMix, e => e[mixMetric.value]),
                e => e.name,
                e => e.color
            )
        }
    ]
}));
/** 碳减排来源(饼) */
/** 碳减排来源(饼):碳值可为负,按绝对值展示减排量 */
const carbonOption = computed(() => ({
    tooltip: { trigger: 'item', formatter: '{b}<br/>{c} kgCO₂e ({d}%)', ...tooltip() },
    tooltip: { trigger: 'item', ...tooltip(), formatter: pieTooltipFormatter('kgCO₂e') },
    series: [
        {
            type: 'pie',
@@ -171,11 +172,11 @@
            padAngle: 3,
            itemStyle: { borderRadius: 6 },
            label: { color: textColor.value, formatter: '{b}\n{d}%' },
            data: data.value.recycleItems.map(e => ({
                name: e.name + '回收',
                value: e.carbon,
                itemStyle: { color: e.color }
            }))
            data: toPieData(
                chartSeries(data.value.recycleItems, e => e.carbon, { carbon: true }),
                e => e.name + '回收',
                e => e.color
            )
        }
    ]
}));
@@ -208,8 +209,12 @@
/** 各类回收量(横向条形) */
const itemOption = computed(() => {
    // 反转让数值最大的排在顶部
    const rows = [...data.value.recycleItems].reverse();
    const carbon = itemMetric.value === 'carbon';
    const rows = chartSeries(
        data.value.recycleItems,
        e => Number(e[itemMetric.value] || 0),
        { carbon, evenSplit: false }
    ).reverse();
    return {
        grid: { containLabel: true, left: 8, right: 24, top: 12, bottom: 8 },
        tooltip: {
@@ -229,7 +234,7 @@
        },
        yAxis: {
            type: 'category',
            data: rows.map(e => e.name),
            data: rows.map(e => e.item.name),
            axisLabel: { color: textColor.value },
            axisLine: { show: false },
            axisTick: { show: false }
@@ -238,11 +243,10 @@
            {
                type: 'bar',
                barWidth: 14,
                data: rows.map(e => Number(e[itemMetric.value] || 0)),
                data: rows.map(e => e.value),
                itemStyle: {
                    borderRadius: [0, 8, 8, 0],
                    // 沿用各类自身配色,与左侧碳减排饼图视觉一致
                    color: (params: any) => rows[params.dataIndex]?.color || primary.value
                    color: (params: any) => rows[params.dataIndex]?.item.color || primary.value
                }
            }
        ]
src/modules/report/views/screen/overview.vue
@@ -52,6 +52,7 @@
import PanelCard from '../../components/panel-card.vue';
import ScreenCameraPreview from '../../components/screen-camera-preview.vue';
import { TIME_RANGE_OPTIONS } from '../../data/screen';
import { chartSeries, pieTooltipFormatter, toPieData } from '../../data/screen-chart';
import { useChartTheme, useScreenData, useScreenFieldSource } from '../../hooks/use-screen';
const { data, timeRange } = useScreenData();
@@ -62,15 +63,15 @@
const trendMetric = ref<'weight' | 'value' | 'carbon'>('weight');
const mixOptions = [
    { label: '重量', value: 'weight' },
    { label: '价值', value: 'value' },
    { label: '数量', value: 'count' }
    { label: '重量(kg)', value: 'weight' },
    { label: '价值(元)', value: 'value' },
    { label: '计数(次)', value: 'count' }
];
const trendOptions = [
    { label: '重量', value: 'weight' },
    { label: '价值', value: 'value' },
    { label: '碳减排', value: 'carbon' }
    { label: '重量(kg)', value: 'weight' },
    { label: '价值(元)', value: 'value' },
    { label: '碳减排(kgCO₂e)', value: 'carbon' }
];
const rangeLabel = computed(
@@ -78,17 +79,17 @@
);
const kpis = computed(() => [
    { field: 'kpis.periodWeightTon', label: `${rangeLabel.value}回收量`, value: data.value.kpis.periodWeightTon, suffix: '吨', fixed: 2, hint: '当前时间范围' },
    { field: 'kpis.periodWeightTon', label: `${rangeLabel.value}回收量`, value: data.value.kpis.periodWeightTon, suffix: 't', fixed: 2, hint: '当前时间范围' },
    { field: 'kpis.resourceRate', label: '资源化率', value: data.value.kpis.resourceRate, suffix: '%', fixed: 1, hint: '可回收物重量/总重量,不含大件' },
    { field: 'kpis.periodValue', label: '回收价值', value: data.value.kpis.periodValue, suffix: '元', fixed: 0, hint: '日表估算销售额' },
    { field: 'kpis.periodCarbonTon', label: '碳减排', value: data.value.kpis.periodCarbonTon, suffix: '吨CO₂e', fixed: 2, hint: '分类重量 × 碳减因数' },
    { field: 'kpis.totalWeightTon', label: '累计回收量', value: data.value.kpis.totalWeightTon, suffix: '吨', fixed: 1, hint: '上线以来累计' },
    { field: 'kpis.totalParticipants', label: '累计参与人数', value: data.value.kpis.totalParticipants, suffix: '人', fixed: 0, hint: '去重校园用户' },
    { field: 'kpis.totalPoints', label: '累计碳积分', value: data.value.kpis.totalPoints, suffix: '分', fixed: 0, hint: '账户余额合计' }
    { field: 'kpis.periodCarbonTon', label: '碳减排', value: data.value.kpis.periodCarbonTon, suffix: 'tCO₂e', fixed: 2, hint: '分类重量 × 碳减因数' },
    { field: 'kpis.totalWeightTon', label: '累计回收量', value: data.value.kpis.totalWeightTon, suffix: 't', fixed: 1, hint: '上线以来累计' },
    { field: 'kpis.totalPoints', label: '累计碳积分', value: data.value.kpis.totalPoints, suffix: '分', fixed: 0, hint: '账户余额合计' },
    { field: 'kpis.totalParticipants', label: '累计参与人数', value: data.value.kpis.totalParticipants, suffix: '人', fixed: 0, hint: '去重校园用户' }
]);
const mixOption = computed(() => ({
    tooltip: { trigger: 'item', formatter: '{b}<br/>{c} ({d}%)', ...tooltip() },
    tooltip: { trigger: 'item', ...tooltip(), formatter: pieTooltipFormatter() },
    legend: { bottom: 0, textStyle: { color: mutedColor.value } },
    series: [
        {
@@ -98,11 +99,11 @@
            padAngle: 3,
            itemStyle: { borderRadius: 6 },
            label: { color: textColor.value, formatter: '{b}\n{d}%' },
            data: data.value.wasteMix.map(e => ({
                name: e.name,
                value: e[mixMetric.value],
                itemStyle: { color: e.color }
            }))
            data: toPieData(
                chartSeries(data.value.wasteMix, e => e[mixMetric.value]),
                e => e.name,
                e => e.color
            )
        }
    ]
}));
src/modules/report/views/screen/participation.vue
@@ -96,7 +96,7 @@
    { label: '回收重量', value: 'weight' },
    { label: '人均回收量', value: 'weightPerCapita' },
    { label: '人均积分', value: 'pointPerCapita' },
    { label: '碳减排', value: 'carbon' }
    { label: '碳减排(kgCO₂e)', value: 'carbon' }
];
const rangeLabel = computed(
src/modules/report/views/screen/resource.vue
@@ -35,6 +35,7 @@
import { computed } from 'vue';
import KpiCard from '../../components/kpi-card.vue';
import PanelCard from '../../components/panel-card.vue';
import { chartSeries, pieTooltipFormatter, toPieData } from '../../data/screen-chart';
import { useChartTheme, useScreenData, useScreenFieldSource } from '../../hooks/use-screen';
const { data } = useScreenData();
@@ -50,7 +51,7 @@
]);
const pieOption = computed(() => ({
    tooltip: { trigger: 'item', formatter: '{b}<br/>{c} kg ({d}%)', ...tooltip() },
    tooltip: { trigger: 'item', ...tooltip(), formatter: pieTooltipFormatter('kg') },
    legend: { bottom: 0, textStyle: { color: mutedColor.value } },
    series: [
        {
@@ -60,14 +61,18 @@
            padAngle: 3,
            itemStyle: { borderRadius: 6 },
            label: { color: textColor.value, formatter: '{b} {d}%' },
            data: data.value.recycleItems.map(e => ({
                name: e.name,
                value: e.weight,
                itemStyle: { color: e.color }
            }))
            data: toPieData(
                chartSeries(data.value.recycleItems, e => e.weight),
                e => e.name,
                e => e.color
            )
        }
    ]
}));
const weightRows = computed(() =>
    chartSeries(data.value.recycleItems, e => e.weight, { evenSplit: false }).reverse()
);
const weightOption = computed(() => ({
    grid: { containLabel: true, left: 8, right: 28, top: 12, bottom: 8 },
@@ -82,7 +87,7 @@
    },
    yAxis: {
        type: 'category',
        data: data.value.recycleItems.map(e => e.name).reverse(),
        data: weightRows.value.map(e => e.item.name),
        axisLabel: { color: textColor.value },
        axisLine: { show: false },
        axisTick: { show: false }
@@ -91,19 +96,24 @@
        {
            type: 'bar',
            barWidth: 16,
            data: data.value.recycleItems
                .map(e => ({ value: e.weight, itemStyle: { color: e.color, borderRadius: [0, 8, 8, 0] } }))
                .reverse()
            data: weightRows.value.map(e => ({
                value: e.value,
                itemStyle: { color: e.item.color, borderRadius: [0, 8, 8, 0] }
            }))
        }
    ]
}));
const valueRows = computed(() =>
    chartSeries(data.value.recycleItems, e => e.value, { evenSplit: false })
);
const valueOption = computed(() => ({
    grid: { containLabel: true, left: 16, right: 16, top: 28, bottom: 8 },
    tooltip: { trigger: 'axis', ...tooltip() },
    xAxis: {
        type: 'category',
        data: data.value.recycleItems.map(e => e.name),
        data: valueRows.value.map(e => e.item.name),
        axisLabel: { color: mutedColor.value },
        axisLine: { show: false },
        axisTick: { show: false }
@@ -121,7 +131,7 @@
        {
            type: 'bar',
            barWidth: 28,
            data: data.value.recycleItems.map(e => e.value),
            data: valueRows.value.map(e => e.value),
            itemStyle: { color: primary.value, borderRadius: [6, 6, 0, 0] }
        }
    ]