wangrong
7 天以前 55c6624407e8ae9d8ba9dd4c65d4c1d3b0ffaed0
src/modules/report/service/screen.ts
@@ -17,6 +17,7 @@
import { UserInfoEntity } from '../../user/entity/info';
import { PushVehicleGpsEntity } from '../../push/entity/vehiclegps';
import { BaseSysParamEntity } from '../../base/entity/sys/param';
import { BaseSysDepartmentEntity } from '../../base/entity/sys/department';
const WASTE_COLORS: Record<string, string> = {
  SW62: '#16a34a',
@@ -278,6 +279,102 @@
    };
  }
  /**
   * 分类下钻:不传 departmentId 按部门汇总该类指标;传入则下钻该部门下各站点。
   * metric: weight | value | count | carbon
   */
  async drill(range: string, code: string, metric: string, departmentId?: number) {
    const dates = this.getRange(range || 'month');
    const rawCode = String(code || '').toUpperCase();
    const field = this.resolveDrillField(rawCode, metric);
    const names = await this.wasteNames();
    const categoryName = names.get(rawCode) || this.defaultWasteName(rawCode);
    const deptId =
      departmentId == null || departmentId === ('' as any)
        ? undefined
        : Number(departmentId);
    const level = Number.isFinite(deptId as number) ? 'site' : 'department';
    if (!field) {
      return {
        level,
        code: rawCode,
        categoryName,
        departmentId: Number.isFinite(deptId as number) ? deptId : null,
        departmentName: null,
        items: [],
      };
    }
    const expr = `SUM(IFNULL(a.${field},0))`;
    const qb = this.dailySite.createQueryBuilder('a');
    let departmentName: string | null = null;
    if (level === 'site') {
      qb.select('a.siteId', 'id')
        .addSelect('a.siteName', 'name')
        .addSelect(expr, 'metricValue')
        .where('a.date BETWEEN :start AND :end', { start: dates.start, end: dates.end })
        .andWhere('a.departmentId = :deptId', { deptId })
        .groupBy('a.siteId')
        .addGroupBy('a.siteName');
      const dept = await this.dailySite.manager
        .createQueryBuilder(BaseSysDepartmentEntity, 'd')
        .select('d.name', 'name')
        .where('d.id = :id', { id: deptId })
        .getRawOne();
      departmentName = dept?.name || `部门${deptId}`;
    } else {
      qb.select('a.departmentId', 'id')
        .addSelect(`IFNULL(d.name, CONCAT('部门', a.departmentId))`, 'name')
        .addSelect(expr, 'metricValue')
        .leftJoin(BaseSysDepartmentEntity, 'd', 'd.id = a.departmentId')
        .where('a.date BETWEEN :start AND :end', { start: dates.start, end: dates.end })
        .groupBy('a.departmentId')
        .addGroupBy('d.name');
    }
    this.applyDept(qb, 'a');
    const rows = await qb.getRawMany();
    const absCarbon = metric === 'carbon';
    const decimals = metric === 'value' || metric === 'count' ? 0 : 1;
    const items = rows
      .map(r => {
        const raw = n(r.metricValue);
        const value = round(absCarbon ? Math.abs(raw) : raw, decimals);
        return {
          id: n(r.id),
          name: String(r.name || (level === 'site' ? `站点${r.id}` : `部门${r.id}`)),
          value,
        };
      })
      .filter(e => e.value !== 0)
      .sort((a, b) => b.value - a.value);
    const total = items.reduce((s, e) => s + e.value, 0);
    return {
      level,
      code: rawCode,
      categoryName,
      departmentId: Number.isFinite(deptId as number) ? deptId : null,
      departmentName,
      items: items.map(e => ({
        ...e,
        percent: total ? round((e.value / total) * 100, 1) : 0,
      })),
    };
  }
  private resolveDrillField(code: string, metric: string): string | null {
    const f = WASTE_FIELDS.find(e => e.code === code);
    if (!f) return null;
    if (metric === 'weight') return f.weight;
    if (metric === 'carbon') return f.carbon;
    if (metric === 'count') return f.time;
    if (metric === 'value') return f.sales || null;
    return null;
  }
  private deptIds(): number[] | null {
    const admin = this.ctx.admin;
    if (!admin || admin.username === 'admin') return null;