wangrong
7 天以前 55c6624407e8ae9d8ba9dd4c65d4c1d3b0ffaed0
首页图表做下尊
3个文件已修改
112 ■■■■■ 已修改文件
src/modules/base/middleware/authority.ts 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/report/controller/admin/screen.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/report/service/screen.ts 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/base/middleware/authority.ts
@@ -101,8 +101,9 @@
            new RegExp(`^${adminUrl}?.*/comm/`).test(url) ||
            // 字典接口
            url == '/admin/dict/info/data' ||
            // 驾驶舱首页汇总:菜单「首页」没有按钮权限,未登记 report:screen:query 时非超管会 403
            url == '/admin/report/screen/query'
            // 驾驶舱首页:菜单「首页」没有按钮权限,未登记 report:screen:* 时非超管会 403
            url == '/admin/report/screen/query' ||
            url == '/admin/report/screen/drill'
          ) {
            await next();
            return;
src/modules/report/controller/admin/screen.ts
@@ -14,4 +14,14 @@
  async query(@Body('range') range: string) {
    return this.ok(await this.reportScreenService.query(range));
  }
  @Post('/drill', { summary: '分类下钻部门/站点' })
  async drill(
    @Body('range') range: string,
    @Body('code') code: string,
    @Body('metric') metric: string,
    @Body('departmentId') departmentId?: number
  ) {
    return this.ok(await this.reportScreenService.drill(range, code, metric, departmentId));
  }
}
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;