src/views/dashboard/analytics/index.vue
@@ -1,25 +1,31 @@
<script lang="ts" setup>
import type { PageParam, PageResult } from '@vben/request';
import type { ErpPurchaseOrderApi } from '#/api/erp/purchase/order';
import type { ErpSaleOrderApi } from '#/api/erp/sale/order';
import type { MesHomeApi } from '#/api/mes/home';
import { computed, onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import { Page } from '@vben/common-ui';
import { Card, Col, Row } from 'ant-design-vue';
import { Col, Row } from 'ant-design-vue';
import { getPurchaseOrderPage } from '#/api/erp/purchase/order';
import { getSaleOrderPage } from '#/api/erp/sale/order';
import { getHomeSummary } from '#/api/mes/home';
import SummaryCard from '#/views/erp/home/modules/summary-card.vue';
import { defaultSummary } from './data';
import AiInsights from './modules/ai-insights.vue';
import AlertPanel from './modules/alert-panel.vue';
import BizCards from './modules/biz-cards.vue';
import KpiCards from './modules/kpi-cards.vue';
import ProductionTrend from './modules/production-trend.vue';
import QuickActions from './modules/quick-actions.vue';
defineOptions({ name: 'DashboardAnalytics' });
const router = useRouter();
const summary = ref(defaultSummary);
const greeting = computed(() => {
  const hour = new Date().getHours();
@@ -41,12 +47,104 @@
  router.push({ name });
}
async function loadSummary() {
  summary.value = await getHomeSummary();
interface PriceSummary {
  monthPrice: number;
  todayPrice: number;
  yearPrice: number;
  yesterdayPrice: number;
}
const saleSummary = ref<PriceSummary>();
const purchaseSummary = ref<PriceSummary>();
const homeSummary = ref<MesHomeApi.Summary>(defaultSummary);
const PAGE_SIZE = 200;
/** 逐页拉取全部分页数据(后端 pageSize 上限 200) */
async function fetchAllPages<T>(
  fetcher: (params: PageParam) => Promise<PageResult<T>>,
): Promise<T[]> {
  const first = await fetcher({ pageNo: 1, pageSize: PAGE_SIZE });
  const list: T[] = [...(first.list ?? [])];
  const total = first.total ?? list.length;
  const pageCount = Math.ceil(total / PAGE_SIZE);
  for (let pageNo = 2; pageNo <= pageCount; pageNo++) {
    const res = await fetcher({ pageNo, pageSize: PAGE_SIZE });
    list.push(...(res.list ?? []));
  }
  return list;
}
function toDayStr(time?: Date | string): string {
  if (!time) return '';
  if (typeof time === 'string') return time.slice(0, 10);
  const d = new Date(time);
  if (Number.isNaN(d.getTime())) return '';
  const m = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  return `${d.getFullYear()}-${m}-${day}`;
}
/** 按订单时间聚合金额:今日 / 昨日 / 本月 / 今年 */
function aggregatePrice(
  orders: Array<{ orderTime?: Date | string; totalPrice?: number }>,
): PriceSummary {
  const now = new Date();
  const today = toDayStr(now);
  const yesterdayDate = new Date(now);
  yesterdayDate.setDate(now.getDate() - 1);
  const yesterday = toDayStr(yesterdayDate);
  const month = today.slice(0, 7);
  const year = today.slice(0, 4);
  const summary: PriceSummary = {
    monthPrice: 0,
    todayPrice: 0,
    yearPrice: 0,
    yesterdayPrice: 0,
  };
  for (const order of orders) {
    const day = toDayStr(order.orderTime);
    if (!day) continue;
    const price = order.totalPrice ?? 0;
    if (day === today) summary.todayPrice += price;
    if (day === yesterday) summary.yesterdayPrice += price;
    if (day.startsWith(month)) summary.monthPrice += price;
    if (day.startsWith(year)) summary.yearPrice += price;
  }
  return summary;
}
async function loadStatistics() {
  try {
    const saleOrders = await fetchAllPages<ErpSaleOrderApi.SaleOrder>(
      getSaleOrderPage,
    );
    saleSummary.value = aggregatePrice(saleOrders);
  } catch {
    /* 接口异常或无权限 */
  }
  try {
    const purchaseOrders = await fetchAllPages<ErpPurchaseOrderApi.PurchaseOrder>(
      getPurchaseOrderPage,
    );
    purchaseSummary.value = aggregatePrice(purchaseOrders);
  } catch {
    /* 接口异常或无权限 */
  }
}
async function loadHomeSummary() {
  try {
    homeSummary.value = await getHomeSummary();
  } catch {
    /* 接口异常或无权限 */
  }
}
onMounted(() => {
  loadSummary();
  loadHomeSummary();
  loadStatistics();
});
</script>
@@ -71,35 +169,19 @@
      </div>
    </div>
    <!-- MES 生产 KPI -->
    <KpiCards :summary="summary" @navigate="handleNavigate" />
    <!-- 质量 / 设备 KPI -->
    <KpiCards :summary="homeSummary" @navigate="handleNavigate" />
    <!-- ERP/WMS/BPM 经营 KPI -->
    <BizCards @navigate="handleNavigate" />
    <!-- AI 智能分析 -->
    <AiInsights
      :active-order-count="summary.workOrderActiveCount"
      @navigate="handleNavigate"
    />
    <!-- 生产趋势 + 预警面板 -->
    <Row :gutter="16" class="mb-4">
      <Col :lg="14" :md="24" :sm="24" :xl="14" :xs="24" class="mb-4">
        <div class="glass-card rounded-2xl p-5">
          <h3 class="mb-4 text-base font-semibold text-gray-800 dark:text-gray-100">
            生产趋势(近7天)
          </h3>
          <ProductionTrend />
        </div>
      </Col>
      <Col :lg="10" :md="24" :sm="24" :xl="10" :xs="24" class="mb-4">
        <AlertPanel
          :repair-active-count="summary.repairActiveCount"
          @navigate="handleNavigate"
        />
      </Col>
    </Row>
    <!-- 销售 / 采购 金额概览 -->
    <div class="mb-4">
      <SummaryCard
        :purchase-summary="purchaseSummary"
        :sale-summary="saleSummary"
      />
    </div>
    <!-- 快捷操作 -->
    <Row :gutter="16" class="mb-4">