瀏覽代碼

瑞都日报汇总添加非生产时效

Zimo 3 天之前
父節點
當前提交
871d45d032

+ 4 - 0
src/api/pms/iotrhdailyreport/index.ts

@@ -69,6 +69,10 @@ export const IotRhDailyReportApi = {
     return await request.get({ url: `/pms/iot-rh-daily-report/polylineStatistics`, params })
   },
 
+  nptStatistics: async (params: any) => {
+    return await request.get({ url: `/pms/iot-rd-daily-report/nptStatistics`, params })
+  },
+
   // 累计工作量统计
   totalWorkload: async (params: any): Promise<IotRhDailyReportTotalWorkloadVO> => {
     return await request.get({ url: `/pms/iot-rh-daily-report/totalWorkload`, params })

+ 551 - 0
src/views/pms/iotrddailyreport/components/DailyStatistics.vue

@@ -0,0 +1,551 @@
+<script setup lang="ts">
+import dayjs from 'dayjs'
+import { IotRdDailyReportApi } from '@/api/pms/iotrddailyreport'
+import { useDebounceFn } from '@vueuse/core'
+import CountTo from '@/components/count-to1.vue'
+import * as echarts from 'echarts'
+import { Motion, AnimatePresence } from 'motion-v'
+import download from '@/utils/download'
+import { useTableComponents } from '@/components/ZmTable/useTableComponents'
+
+interface Query {
+  pageNo: number
+  pageSize: number
+  deptId: number
+  contractName?: string
+  taskName?: string
+  createTime: string[]
+}
+
+const props = defineProps<{
+  query: Query
+  deptName: string
+  refreshKey: number
+}>()
+
+const totalWorkKeys: [string, string | undefined, string, string, number][] = [
+  [
+    'cumulativeBridgePlug',
+    undefined,
+    '桥塞',
+    'i-material-symbols:check-circle-outline-rounded text-emerald',
+    0
+  ],
+  [
+    'cumulativeRunCount',
+    undefined,
+    '趟数',
+    'i-material-symbols:check-circle-outline-rounded text-emerald',
+    0
+  ],
+  [
+    'cumulativeWorkingWell',
+    undefined,
+    '井数',
+    'i-material-symbols:check-circle-outline-rounded text-emerald',
+    0
+  ],
+  [
+    'cumulativeWorkingLayers',
+    undefined,
+    '段数',
+    'i-material-symbols:check-circle-outline-rounded text-emerald',
+    0
+  ],
+  [
+    'cumulativeHourCount',
+    undefined,
+    '小时(H)',
+    'i-material-symbols:nest-clock-farsight-analog-outline-rounded text-emerald',
+    2
+  ],
+  [
+    'cumulativeWaterVolume',
+    '方',
+    '水方量',
+    'i-material-symbols:water-drop-outline-rounded text-sky',
+    2
+  ],
+  ['taici', undefined, '台次', 'i-material-symbols:check-circle-outline-rounded text-emerald', 0],
+  [
+    'utilizationRate',
+    '%',
+    '设备利用率',
+    'i-material-symbols:check-circle-outline-rounded text-emerald',
+    0
+  ],
+  [
+    'cumulativeFuels',
+    '万升',
+    '累计油耗',
+    'i-material-symbols:directions-car-outline-rounded text-sky',
+    2
+  ]
+]
+
+const totalWork = ref<Record<string, number>>({
+  cumulativeFuels: 0,
+  taici: 0,
+  cumulativeBridgePlug: 0,
+  cumulativeRunCount: 0,
+  cumulativeWorkingWell: 0,
+  cumulativeWorkingLayers: 0,
+  cumulativeHourCount: 0,
+  cumulativeWaterVolume: 0,
+  utilizationRate: 0
+})
+
+const totalLoading = ref(false)
+
+const getQueryWithoutPage = () => {
+  const { pageNo: _pageNo, pageSize: _pageSize, ...other } = props.query
+
+  void _pageNo
+  void _pageSize
+
+  return other
+}
+
+const getTotal = useDebounceFn(async () => {
+  totalLoading.value = true
+
+  try {
+    const res2 = await IotRdDailyReportApi.totalWorkload(getQueryWithoutPage())
+
+    totalWork.value = {
+      ...totalWork.value,
+      taici: res2.taici || 0,
+      cumulativeBridgePlug: res2.cumulativeBridgePlug || 0,
+      cumulativeRunCount: res2.cumulativeRunCount || 0,
+      cumulativeWorkingWell: res2.cumulativeWorkingWell || 0,
+      cumulativeWorkingLayers: res2.cumulativeWorkingLayers || 0,
+      cumulativeHourCount: res2.cumulativeHourCount || 0,
+      cumulativeWaterVolume: res2.cumulativeWaterVolume || 0,
+      ...res2,
+      cumulativeFuels: (res2.cumulativeFuels || 0) / 10000,
+      utilizationRate: Number(((res2.utilizationRate || 0) * 100).toFixed(2))
+    }
+  } finally {
+    totalLoading.value = false
+  }
+}, 500)
+
+interface List {
+  id: number | null
+  name: string | null
+  type: '1' | '2' | '3'
+  cumulativeBridgePlug: number | null
+  cumulativeRunCount: number | null
+  cumulativeWorkingWell: number | null
+  cumulativeHourCount: number | null
+  totalDailyFuel: number | null
+  cumulativeWaterVolume: number | null
+  cumulativeWorkingLayers: number | null
+  cumulativePumpTrips: number | null
+  cumulativeMixSand: number | null
+  utilizationRate: number | null
+}
+
+const list = ref<List[]>([])
+
+const listLoading = ref(false)
+
+const getList = useDebounceFn(async () => {
+  listLoading.value = true
+  try {
+    const res = await IotRdDailyReportApi.getIotRdDailyReportSummary(props.query)
+    const reslist = res?.list || []
+
+    list.value = reslist.map(
+      ({ projectDeptId, projectDeptName, teamId, teamName, type, ...other }) => {
+        return {
+          id: type === '2' ? projectDeptId : teamId,
+          name: type === '2' ? projectDeptName : teamName,
+          ...other,
+          cumulativeBridgePlug: other.cumulativeBridgePlug || 0,
+          cumulativeRunCount: other.cumulativeRunCount || 0,
+          cumulativeWorkingWell: other.cumulativeWorkingWell || 0,
+          cumulativeHourCount: other.cumulativeHourCount || 0,
+          totalDailyFuel: other.totalDailyFuel || 0,
+          cumulativeWaterVolume: other.cumulativeWaterVolume || 0,
+          cumulativeWorkingLayers: other.cumulativeWorkingLayers || 0,
+          cumulativePumpTrips: other.cumulativePumpTrips || 0,
+          cumulativeMixSand: other.cumulativeMixSand || 0,
+          utilizationRate: other.utilizationRate || 0
+        }
+      }
+    )
+  } finally {
+    listLoading.value = false
+  }
+}, 500)
+
+const tab = ref<'表格' | '看板'>('表格')
+
+const currentTab = ref<'表格' | '看板'>('表格')
+
+const direction = ref<'left' | 'right'>('right')
+
+const handleSelectTab = (val: '表格' | '看板') => {
+  tab.value = val
+  direction.value = val === '看板' ? 'right' : 'left'
+  nextTick(() => {
+    currentTab.value = val
+    setTimeout(() => {
+      render()
+    })
+  })
+}
+
+const chartRef = ref<HTMLDivElement | null>(null)
+let chart: echarts.ECharts | null = null
+
+const xAxisData = ref<string[]>([])
+
+const legend = ref<string[][]>([
+  ['个数', 'cumulativeBridgePlug'],
+  ['井数', 'cumulativeWorkingWell'],
+  ['小时 (H)', 'cumulativeHourCount'],
+  ['油耗 (万升)', 'cumulativeFuels'],
+  ['水方量 (方)', 'cumulativeWaterVolume'],
+  ['台次(泵车)', 'cumulativePumpTrips'],
+  ['段数', 'cumulativeWorkingLayers'],
+  ['台次(仪表/混砂)', 'cumulativeMixSand'],
+  ['设备利用率 (%)', 'utilizationRate']
+])
+
+const chartData = ref<Record<string, number[]>>({
+  cumulativeBridgePlug: [],
+  cumulativeWorkingWell: [],
+  cumulativeHourCount: [],
+  cumulativeFuels: [],
+  cumulativeWaterVolume: [],
+  cumulativeWorkingLayers: [],
+  cumulativePumpTrips: [],
+  cumulativeMixSand: [],
+  utilizationRate: []
+})
+
+let chartLoading = ref(false)
+
+const getChart = useDebounceFn(async () => {
+  chartLoading.value = true
+
+  try {
+    const res = await IotRdDailyReportApi.getIotRdDailyReportSummaryPolyline(props.query)
+
+    chartData.value = {
+      cumulativeBridgePlug: res.map((item) => item.cumulativeBridgePlug || 0),
+      cumulativeWorkingWell: res.map((item) => item.cumulativeWorkingWell || 0),
+      cumulativeHourCount: res.map((item) => item.cumulativeHourCount || 0),
+      cumulativeFuels: res.map((item) => (item.cumulativeFuels || 0) / 10000),
+      cumulativeWaterVolume: res.map((item) => item.cumulativeWaterVolume || 0),
+      cumulativeWorkingLayers: res.map((item) => item.cumulativeWorkingLayers || 0),
+      cumulativePumpTrips: res.map((item) => item.cumulativePumpTrips || 0),
+      cumulativeMixSand: res.map((item) => item.cumulativeMixSand || 0),
+      utilizationRate: res.map((item) => Number(((item.utilizationRate || 0) * 100).toFixed(2)))
+    }
+
+    xAxisData.value = res.map((item) => item.reportDate || '')
+  } finally {
+    chartLoading.value = false
+  }
+}, 500)
+
+const resizer = () => {
+  chart?.resize()
+}
+
+onUnmounted(() => {
+  window.removeEventListener('resize', resizer)
+  chart?.dispose()
+})
+
+const render = () => {
+  if (!chartRef.value) return
+
+  chart?.dispose()
+  chart = echarts.init(chartRef.value, undefined, { renderer: 'canvas' })
+
+  window.removeEventListener('resize', resizer)
+  window.addEventListener('resize', resizer)
+
+  const values: number[] = []
+
+  for (const [_name, key] of legend.value) {
+    values.push(...(chartData.value[key] || []))
+  }
+
+  const maxVal = values.length === 0 ? 10000 : Math.max(...values)
+  const minVal = values.length === 0 ? 0 : Math.min(...values) > 0 ? 0 : Math.min(...values)
+
+  const maxDigits = (Math.floor(maxVal) + '').length
+  const minDigits = minVal === 0 ? 0 : (Math.floor(Math.abs(minVal)) + '').length
+  const interval = Math.max(maxDigits, minDigits)
+
+  const maxInterval = interval
+  const minInterval = minDigits
+
+  const intervalArr = [0]
+  for (let i = 1; i <= interval; i++) {
+    intervalArr.push(Math.pow(10, i))
+  }
+
+  chart.setOption({
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'line'
+      },
+      formatter: (params) => {
+        let d = `${params[0].axisValueLabel}<br>`
+        let item = params.map((el) => {
+          const key = legend.value[el.componentIndex]?.[1] || ''
+          const value = chartData.value[key]?.[el.dataIndex] || 0
+
+          return `<div class="flex items-center justify-between mt-1 gap-1">
+            <span>${el.marker} ${el.seriesName}</span>
+            <span>${value.toFixed(2)} ${el.seriesName.split(' ')[1] ?? ''}</span>
+          </div>`
+        })
+
+        return d + item.join('')
+      }
+    },
+    legend: {
+      data: legend.value.map(([name]) => name),
+      show: true
+    },
+    xAxis: {
+      type: 'category',
+      data: xAxisData.value
+    },
+    yAxis: {
+      type: 'value',
+      min: -minInterval,
+      max: maxInterval,
+      interval: 1,
+      axisLabel: {
+        formatter: (v) => {
+          const num = v === 0 ? 0 : v > 0 ? Math.pow(10, v) : -Math.pow(10, -v)
+
+          return num.toLocaleString()
+        }
+      }
+    },
+    series: legend.value.map(([name, key]) => ({
+      name,
+      type: 'line',
+      smooth: true,
+      showSymbol: true,
+      data: (chartData.value[key] || []).map((value) => {
+        if (value === 0) return 0
+
+        const isPositive = value > 0
+        const absItem = Math.abs(value)
+
+        const min_value = Math.max(...intervalArr.filter((v) => v <= absItem))
+        const min_index = intervalArr.findIndex((v) => v === min_value)
+
+        const new_value =
+          (absItem - min_value) / (intervalArr[min_index + 1] - intervalArr[min_index]) + min_index
+
+        return isPositive ? new_value : -new_value
+      })
+    }))
+  })
+}
+
+const handleQuery = () => {
+  getChart().then(() => {
+    nextTick(() => {
+      render()
+    })
+  })
+  getList()
+  getTotal()
+}
+
+watch(
+  () => [
+    props.refreshKey,
+    props.query.deptId,
+    props.query.contractName,
+    props.query.taskName,
+    props.query.createTime?.[0],
+    props.query.createTime?.[1]
+  ],
+  () => {
+    handleQuery()
+  },
+  { immediate: true }
+)
+
+const exportChart = () => {
+  if (!chart) return
+  let img = new Image()
+  img.src = chart.getDataURL({
+    type: 'png',
+    pixelRatio: 1,
+    backgroundColor: '#fff'
+  })
+
+  img.onload = function () {
+    let canvas = document.createElement('canvas')
+    canvas.width = img.width
+    canvas.height = img.height
+    let ctx = canvas.getContext('2d')
+    ctx?.drawImage(img, 0, 0)
+    let dataURL = canvas.toDataURL('image/png')
+
+    let a = document.createElement('a')
+
+    let event = new MouseEvent('click')
+
+    a.href = dataURL
+    a.download = `瑞都日报统计数据.png`
+    a.dispatchEvent(event)
+  }
+}
+
+const exportData = async () => {
+  const res = await IotRdDailyReportApi.exportRdDailyReportStatistics(props.query)
+  download.excel(res, '瑞都日报统计数据.xlsx')
+}
+
+const exportAll = async () => {
+  if (tab.value === '看板') exportChart()
+  else exportData()
+}
+
+const router = useRouter()
+
+const tolist = (id: number | null) => {
+  if (!id) return
+
+  router.push({
+    path: '/iotdayilyreport/IotRdDailyReport',
+    query: {
+      ...getQueryWithoutPage(),
+      deptId: id
+    }
+  })
+}
+
+const { ZmTable, ZmTableColumn } = useTableComponents()
+</script>
+
+<template>
+  <div class="grid grid-rows-[128px_1fr] gap-4 h-full min-h-0">
+    <div class="grid grid-cols-9 gap-8" v-loading="totalLoading">
+      <div
+        v-for="info in totalWorkKeys"
+        :key="info[0]"
+        class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow p-1 flex flex-col items-center justify-center gap-1"
+      >
+        <div class="size-7.5" :class="info[3]"></div>
+        <count-to
+          class="text-2xl font-medium"
+          :start-val="0"
+          :end-val="totalWork[info[0]]"
+          :decimals="info[4]"
+        >
+          <span class="text-xs leading-8 text-[var(--el-text-color-regular)]">暂无数据</span>
+        </count-to>
+        <div class="text-sm font-medium text-[var(--el-text-color-regular)] whitespace-nowrap">
+          {{ info[1] ? info[2] + '(' + info[1] + ')' : info[2] }}
+        </div>
+      </div>
+    </div>
+
+    <div class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow flex flex-col p-4 gap-2 min-h-0">
+      <div class="flex h-12 items-center justify-between">
+        <el-button-group>
+          <el-button
+            size="default"
+            :type="tab === '表格' ? 'primary' : 'default'"
+            @click="handleSelectTab('表格')"
+            >表格
+          </el-button>
+          <el-button
+            size="default"
+            :type="tab === '看板' ? 'primary' : 'default'"
+            @click="handleSelectTab('看板')"
+            >看板
+          </el-button>
+        </el-button-group>
+        <h3 class="text-xl font-medium">{{ `${deptName}-${tab}` }}</h3>
+        <el-button size="default" type="primary" @click="exportAll">导出</el-button>
+      </div>
+      <div class="flex-1 relative min-h-0">
+        <el-auto-resizer class="absolute">
+          <template #default="{ height }">
+            <Motion
+              as="div"
+              :style="{ position: 'relative', overflow: 'hidden' }"
+              :animate="{ height: `${height}px`, width: `100%` }"
+              :transition="{ type: 'spring', stiffness: 200, damping: 25, duration: 0.3 }"
+            >
+              <AnimatePresence :initial="false" mode="sync">
+                <Motion
+                  :key="currentTab"
+                  as="div"
+                  :initial="{ x: direction === 'left' ? '-100%' : '100%', opacity: 0 }"
+                  :animate="{ x: '0%', opacity: 1 }"
+                  :exit="{ x: direction === 'left' ? '50%' : '-50%', opacity: 0 }"
+                  :transition="{ type: 'tween', stiffness: 300, damping: 30, duration: 0.3 }"
+                  :style="{ position: 'absolute', left: 0, right: 0, top: 0 }"
+                >
+                  <div :style="{ width: `100%`, height: `${height}px` }">
+                    <zm-table
+                      v-if="currentTab === '表格'"
+                      :loading="listLoading"
+                      :data="list"
+                      :height="height"
+                      show-border
+                    >
+                      <zm-table-column label="部门" prop="name">
+                        <template #default="{ row }">
+                          <el-button text type="primary" @click.prevent="tolist(row.id)">{{
+                            row.name
+                          }}</el-button>
+                        </template>
+                      </zm-table-column>
+                      <zm-table-column label="油耗(万升)" prop="totalDailyFuel" />
+                      <zm-table-column label="桥塞" prop="cumulativeBridgePlug" />
+                      <zm-table-column label="趟数" prop="cumulativeRunCount" />
+                      <zm-table-column label="井数" prop="cumulativeWorkingWell" />
+                      <zm-table-column label="段数" prop="cumulativeWorkingLayers" />
+                      <zm-table-column label="小时(H)" prop="cumulativeHourCount" />
+                      <zm-table-column label="水方量(方)" prop="cumulativeWaterVolume" />
+                      <zm-table-column label="台次">
+                        <zm-table-column label="泵车" prop="cumulativePumpTrips" />
+                        <zm-table-column label="仪表/混砂" prop="cumulativeMixSand" />
+                      </zm-table-column>
+                      <zm-table-column
+                        label="设备利用率"
+                        prop="utilizationRate"
+                        cover-formatter
+                        :real-value="
+                          (row: List) => (Number(row.utilizationRate ?? 0) * 100).toFixed(2) + '%'
+                        "
+                      />
+                    </zm-table>
+                    <div
+                      ref="chartRef"
+                      v-loading="chartLoading"
+                      :key="dayjs().valueOf()"
+                      v-else
+                      :style="{ width: `100%`, height: `${height}px` }"
+                    >
+                    </div>
+                  </div>
+                </Motion>
+              </AnimatePresence>
+            </Motion>
+          </template>
+        </el-auto-resizer>
+      </div>
+    </div>
+  </div>
+</template>

+ 358 - 0
src/views/pms/iotrddailyreport/components/NonProductionEfficiency.vue

@@ -0,0 +1,358 @@
+<script setup lang="ts">
+import { IotRhDailyReportApi } from '@/api/pms/iotrhdailyreport'
+import { useTableComponents } from '@/components/ZmTable/useTableComponents'
+import { useDebounceFn } from '@vueuse/core'
+import * as echarts from 'echarts'
+
+interface Query {
+  pageNo: number
+  pageSize: number
+  deptId: number
+  contractName?: string
+  taskName?: string
+  createTime: string[]
+}
+
+const props = defineProps<{
+  query: Query
+  deptName: string
+  refreshKey: number
+}>()
+
+interface ListItem {
+  id?: number
+  teamName?: string
+  projectDeptName?: string
+  accidentTime: number
+  repairTime: number
+  selfStopTime: number
+  complexityTime: number
+  relocationTime: number
+  rectificationTime: number
+  waitingStopTime: number
+  winterBreakTime: number
+  partyaDesign: number
+  partyaPrepare: number
+  partyaResource: number
+  otherNptTime: number
+  nptTotal: number
+  nptRate: number
+  calendarTime: number
+}
+
+const pageNo = ref(1)
+const pageSize = ref(10)
+const list = ref<ListItem[]>([])
+const total = ref(0)
+const loading = ref(false)
+const tab = ref<'表格' | '看板'>('表格')
+const chartRef = ref<HTMLDivElement | null>(null)
+let chart: echarts.ECharts | null = null
+
+const nonProductionTimeFields: [keyof ListItem, string][] = [
+  ['accidentTime', '工程质量'],
+  ['repairTime', '设备故障'],
+  ['selfStopTime', '设备保养'],
+  ['complexityTime', '技术受限'],
+  ['relocationTime', '生产配合'],
+  ['rectificationTime', '生产组织'],
+  ['waitingStopTime', '不可抗力'],
+  ['winterBreakTime', '待命'],
+  ['partyaDesign', '甲方设计'],
+  ['partyaPrepare', '甲方准备'],
+  ['partyaResource', '甲方资源'],
+  ['otherNptTime', '其它']
+]
+
+const getQueryWithoutPage = () => {
+  const { pageNo: _pageNo, pageSize: _pageSize, ...query } = props.query
+
+  void _pageNo
+  void _pageSize
+
+  return query
+}
+
+const normalizePageData = (data: any) => {
+  if (Array.isArray(data)) {
+    return {
+      list: data,
+      total: data.length
+    }
+  }
+
+  const dataList = data?.list || data?.records || []
+
+  return {
+    list: dataList,
+    total: data?.total || dataList.length
+  }
+}
+
+const loadList = useDebounceFn(async function () {
+  loading.value = true
+  try {
+    const data = await IotRhDailyReportApi.nptStatistics({
+      ...getQueryWithoutPage(),
+      pageNo: pageNo.value,
+      pageSize: pageSize.value
+    })
+    const pageData = normalizePageData(data)
+
+    list.value = pageData.list
+    total.value = pageData.total
+
+    if (tab.value === '看板') {
+      nextTick(renderChart)
+    }
+  } finally {
+    loading.value = false
+  }
+}, 500)
+
+function handleSizeChange(val: number) {
+  pageSize.value = val
+  handleQuery()
+}
+
+function handleCurrentChange(val: number) {
+  pageNo.value = val
+  loadList()
+}
+
+function handleQuery() {
+  pageNo.value = 1
+  loadList()
+}
+
+watch(
+  () => [
+    props.refreshKey,
+    props.query.deptId,
+    props.query.contractName,
+    props.query.taskName,
+    props.query.createTime?.[0],
+    props.query.createTime?.[1]
+  ],
+  () => {
+    handleQuery()
+  },
+  { immediate: true }
+)
+
+const handleSelectTab = (val: '表格' | '看板') => {
+  tab.value = val
+
+  if (val === '看板') {
+    nextTick(renderChart)
+  } else {
+    chart?.dispose()
+    chart = null
+  }
+}
+
+const formatNumber = (value: unknown) => {
+  const num = Number(value || 0)
+  return Number.isInteger(num) ? `${num}` : num.toFixed(2)
+}
+
+const formatTeamName = (row: ListItem) => {
+  return row.teamName || row.projectDeptName || '-'
+}
+
+const formatRate = (row: ListItem) => {
+  return `${(Number(row.nptRate || 0) * 100).toFixed(2)}%`
+}
+
+const getPieData = () => {
+  return list.value
+    .map((row) => ({
+      name: formatTeamName(row),
+      value: Number(row.nptTotal || 0)
+    }))
+    .filter((item) => item.value > 0)
+}
+
+const resizeChart = () => {
+  chart?.resize()
+}
+
+const renderChart = () => {
+  if (!chartRef.value) return
+
+  chart?.dispose()
+  chart = echarts.init(chartRef.value, undefined, { renderer: 'canvas' })
+
+  window.removeEventListener('resize', resizeChart)
+  window.addEventListener('resize', resizeChart)
+
+  const pieData = getPieData()
+
+  chart.setOption({
+    tooltip: {
+      trigger: 'item',
+      formatter: '{b}<br/>NPT合计: {c} H<br/>占比: {d}%'
+    },
+    legend: {
+      type: 'scroll',
+      orient: 'vertical',
+      right: 24,
+      top: 32,
+      bottom: 24
+    },
+    graphic:
+      pieData.length === 0
+        ? {
+            type: 'text',
+            left: 'center',
+            top: 'middle',
+            style: {
+              text: '暂无NPT数据',
+              fill: '#909399',
+              fontSize: 14
+            }
+          }
+        : undefined,
+    series: [
+      {
+        name: 'NPT合计',
+        type: 'pie',
+        radius: ['42%', '68%'],
+        center: ['50%', '50%'],
+        avoidLabelOverlap: true,
+        itemStyle: {
+          borderRadius: 4,
+          borderColor: '#fff',
+          borderWidth: 2
+        },
+        label: {
+          formatter: '{b}: {d}%'
+        },
+        data: pieData
+      }
+    ]
+  })
+}
+
+onUnmounted(() => {
+  window.removeEventListener('resize', resizeChart)
+  chart?.dispose()
+})
+
+const { ZmTable, ZmTableColumn } = useTableComponents<ListItem>()
+</script>
+
+<template>
+  <div class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow flex flex-col p-4 gap-2 h-full min-h-0">
+    <div class="flex h-12 items-center justify-between">
+      <el-button-group>
+        <el-button
+          size="default"
+          :type="tab === '表格' ? 'primary' : 'default'"
+          @click="handleSelectTab('表格')"
+          >表格
+        </el-button>
+        <el-button
+          size="default"
+          :type="tab === '看板' ? 'primary' : 'default'"
+          @click="handleSelectTab('看板')"
+          >看板
+        </el-button>
+      </el-button-group>
+      <!-- <h3 class="text-xl font-medium">{{ `${deptName}-${tab}` }}</h3> -->
+      <div class="w-80px"></div>
+    </div>
+
+    <div class="flex-1 relative min-h-0">
+      <el-auto-resizer class="absolute">
+        <template #default="{ width, height }">
+          <zm-table
+            v-if="tab === '表格'"
+            :data="list"
+            :loading="loading"
+            :width="width"
+            :max-height="height"
+            :height="height"
+            show-border
+          >
+            <zm-table-column
+              prop="teamName"
+              label="队伍"
+              min-width="120"
+              fixed="left"
+              cover-formatter
+              :real-value="formatTeamName"
+            />
+            <zm-table-column
+              v-for="[prop, label] in nonProductionTimeFields"
+              :key="prop"
+              :prop="prop"
+              :label="label"
+              min-width="92"
+              cover-formatter
+              :real-value="(row: ListItem) => formatNumber(row[prop])"
+            />
+            <zm-table-column label="npt合计" is-parent>
+              <zm-table-column
+                prop="nptTotal"
+                label="时长(H)"
+                min-width="92"
+                cover-formatter
+                :real-value="(row: ListItem) => formatNumber(row.nptTotal)"
+              />
+              <zm-table-column
+                prop="nptRate"
+                label="占比"
+                min-width="92"
+                cover-formatter
+                :real-value="formatRate"
+              />
+            </zm-table-column>
+            <zm-table-column
+              prop="calendarTime"
+              label="自然时间"
+              min-width="92"
+              cover-formatter
+              :real-value="(row: ListItem) => formatNumber(row.calendarTime)"
+            />
+          </zm-table>
+          <div
+            v-else
+            ref="chartRef"
+            v-loading="loading"
+            class="npt-board-container"
+            :style="{ width: `${width}px`, height: `${height}px` }"
+          >
+          </div>
+        </template>
+      </el-auto-resizer>
+    </div>
+
+    <div class="h-10 flex items-center justify-end">
+      <el-pagination
+        size="default"
+        v-show="tab === '表格' && total > 0"
+        v-model:current-page="pageNo"
+        v-model:page-size="pageSize"
+        :background="true"
+        :page-sizes="[10, 20, 30, 50, 100]"
+        :total="total"
+        layout="total, sizes, prev, pager, next, jumper"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+  </div>
+</template>
+
+<style scoped>
+:deep(.npt-cell-highlight) {
+  background-color: #fff566 !important;
+}
+
+.npt-board-container {
+  background: var(--el-bg-color);
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 4px;
+}
+</style>

+ 43 - 533
src/views/pms/iotrddailyreport/summary.vue

@@ -1,17 +1,9 @@
 <script setup lang="ts">
 import dayjs from 'dayjs'
-import { IotRdDailyReportApi } from '@/api/pms/iotrddailyreport'
-import { useDebounceFn } from '@vueuse/core'
-import CountTo from '@/components/count-to1.vue'
-import * as echarts from 'echarts'
-
-import { Motion, AnimatePresence } from 'motion-v'
-
 import { rangeShortcuts } from '@/utils/formatTime'
-
 import { useUserStore } from '@/store/modules/user'
-import download from '@/utils/download'
-import { useTableComponents } from '@/components/ZmTable/useTableComponents'
+import DailyStatistics from './components/DailyStatistics.vue'
+import NonProductionEfficiency from './components/NonProductionEfficiency.vue'
 
 const deptId = useUserStore().getUser.deptId
 
@@ -26,441 +18,42 @@ interface Query {
 
 const id = deptId
 
-const query = ref<Query>({
+const createDefaultQuery = (): Query => ({
   pageNo: 1,
   pageSize: 10,
   deptId: deptId,
+  contractName: '',
+  taskName: '',
   createTime: [
     ...rangeShortcuts[1].value().map((item) => dayjs(item).format('YYYY-MM-DD HH:mm:ss'))
   ]
 })
 
-const totalWorkKeys: [string, string | undefined, string, string, number][] = [
-  [
-    'cumulativeBridgePlug',
-    undefined,
-    '桥塞',
-    'i-material-symbols:check-circle-outline-rounded text-emerald',
-    0
-  ],
-  [
-    'cumulativeRunCount',
-    undefined,
-    '趟数',
-    'i-material-symbols:check-circle-outline-rounded text-emerald',
-    0
-  ],
-  [
-    'cumulativeWorkingWell',
-    undefined,
-    '井数',
-    'i-material-symbols:check-circle-outline-rounded text-emerald',
-    0
-  ],
-  [
-    'cumulativeWorkingLayers',
-    undefined,
-    '段数',
-    'i-material-symbols:check-circle-outline-rounded text-emerald',
-    0
-  ],
-  [
-    'cumulativeHourCount',
-    undefined,
-    '小时(H)',
-    'i-material-symbols:nest-clock-farsight-analog-outline-rounded text-emerald',
-    2
-  ],
-  [
-    'cumulativeWaterVolume',
-    '方',
-    '水方量',
-    'i-material-symbols:water-drop-outline-rounded text-sky',
-    2
-  ],
-  ['taici', undefined, '台次', 'i-material-symbols:check-circle-outline-rounded text-emerald', 0],
-  [
-    'utilizationRate',
-    '%',
-    '设备利用率',
-    'i-material-symbols:check-circle-outline-rounded text-emerald',
-    0
-  ],
-  [
-    'cumulativeFuels',
-    '万升',
-    '累计油耗',
-    'i-material-symbols:directions-car-outline-rounded text-sky',
-    2
-  ]
-]
-
-const totalWork = ref({
-  cumulativeFuels: 0,
-  taici: 0,
-  cumulativeBridgePlug: 0,
-  cumulativeRunCount: 0,
-  cumulativeWorkingWell: 0,
-  cumulativeWorkingLayers: 0,
-  cumulativeHourCount: 0,
-  cumulativeWaterVolume: 0,
-  utilizationRate: 0
-})
-
-const totalLoading = ref(false)
-
-const getTotal = useDebounceFn(async () => {
-  totalLoading.value = true
-
-  const { pageNo, pageSize, ...other } = query.value
-
-  try {
-    const res2 = await IotRdDailyReportApi.totalWorkload(other)
-
-    totalWork.value = {
-      ...totalWork.value,
-      taici: res2.taici || 0,
-      cumulativeBridgePlug: res2.cumulativeBridgePlug || 0,
-      cumulativeRunCount: res2.cumulativeRunCount || 0,
-      cumulativeWorkingWell: res2.cumulativeWorkingWell || 0,
-      cumulativeWorkingLayers: res2.cumulativeWorkingLayers || 0,
-      cumulativeHourCount: res2.cumulativeHourCount || 0,
-      cumulativeWaterVolume: res2.cumulativeWaterVolume || 0,
-      ...res2,
-      cumulativeFuels: (res2.cumulativeFuels || 0) / 10000,
-      utilizationRate: Number(((res2.utilizationRate || 0) * 100).toFixed(2))
-    }
-  } finally {
-    totalLoading.value = false
-  }
-}, 500)
-
-interface List {
-  id: number | null
-  name: string | null
-  type: '1' | '2' | '3'
-  cumulativeBridgePlug: number | null
-  cumulativeRunCount: number | null
-  cumulativeWorkingWell: number | null
-  cumulativeHourCount: number | null
-  totalDailyFuel: number | null
-  cumulativeWaterVolume: number | null
-  cumulativeWorkingLayers: number | null
-  cumulativePumpTrips: number | null
-  cumulativeMixSand: number | null
-  utilizationRate: number | null
-}
-
-const list = ref<List[]>([])
-
-const type = ref('2')
-
-const listLoading = ref(false)
-
-const getList = useDebounceFn(async () => {
-  listLoading.value = true
-  try {
-    const res = await IotRdDailyReportApi.getIotRdDailyReportSummary(query.value)
-
-    const { list: reslist } = res
-
-    type.value = reslist[0]?.type || '2'
-
-    list.value = reslist.map(
-      ({ id, projectDeptId, projectDeptName, teamId, teamName, sort, taskId, type, ...other }) => {
-        return {
-          id: type === '2' ? projectDeptId : teamId,
-          name: type === '2' ? projectDeptName : teamName,
-          ...other,
-          cumulativeBridgePlug: other.cumulativeBridgePlug || 0,
-          cumulativeRunCount: other.cumulativeRunCount || 0,
-          cumulativeWorkingWell: other.cumulativeWorkingWell || 0,
-          cumulativeHourCount: other.cumulativeHourCount || 0,
-          totalDailyFuel: other.totalDailyFuel || 0,
-          cumulativeWaterVolume: other.cumulativeWaterVolume || 0,
-          cumulativeWorkingLayers: other.cumulativeWorkingLayers || 0,
-          cumulativePumpTrips: other.cumulativePumpTrips || 0,
-          cumulativeMixSand: other.cumulativeMixSand || 0,
-          utilizationRate: other.utilizationRate || 0
-        }
-      }
-    )
-  } finally {
-    listLoading.value = false
-  }
-}, 500)
-
-const tab = ref<'表格' | '看板'>('表格')
-
-const currentTab = ref<'表格' | '看板'>('表格')
-
+const query = ref<Query>(createDefaultQuery())
+const activeTab = ref<'日报统计' | '非生产时效'>('日报统计')
 const deptName = ref('四川瑞都')
-
-const direction = ref<'left' | 'right'>('right')
-
-const handleSelectTab = (val: '表格' | '看板') => {
-  tab.value = val
-  direction.value = val === '看板' ? 'right' : 'left'
-  nextTick(() => {
-    currentTab.value = val
-    setTimeout(() => {
-      render()
-    })
-  })
-}
-
-const chartRef = ref<HTMLDivElement | null>(null)
-let chart: echarts.ECharts | null = null
-
-const xAxisData = ref<string[]>([])
-
-const legend = ref<string[][]>([
-  ['个数', 'cumulativeBridgePlug'],
-  ['井数', 'cumulativeWorkingWell'],
-  ['小时 (H)', 'cumulativeHourCount'],
-  ['油耗 (万升)', 'cumulativeFuels'],
-  ['水方量 (方)', 'cumulativeWaterVolume'],
-  ['台次(泵车)', 'cumulativePumpTrips'],
-  ['段数', 'cumulativeWorkingLayers'],
-  ['台次(仪表/混砂)', 'cumulativeMixSand'],
-  ['设备利用率 (%)', 'utilizationRate']
-])
-
-const chartData = ref<Record<string, number[]>>({
-  cumulativeFuelConsumption: [],
-  cumulativeGasInjection: [],
-  cumulativePowerConsumption: [],
-  cumulativeWaterInjection: [],
-  transitTime: [],
-  utilizationRate: []
-})
-
-let chartLoading = ref(false)
-
-const getChart = useDebounceFn(async () => {
-  chartLoading.value = true
-
-  try {
-    const res = await IotRdDailyReportApi.getIotRdDailyReportSummaryPolyline(query.value)
-
-    chartData.value = {
-      cumulativeBridgePlug: res.map((item) => item.cumulativeBridgePlug || 0),
-      cumulativeWorkingWell: res.map((item) => item.cumulativeWorkingWell || 0),
-      cumulativeHourCount: res.map((item) => item.cumulativeHourCount || 0),
-      cumulativeFuels: res.map((item) => (item.cumulativeFuels || 0) / 10000),
-      cumulativeWaterVolume: res.map((item) => item.cumulativeWaterVolume || 0),
-      cumulativeWorkingLayers: res.map((item) => item.cumulativeWorkingLayers || 0),
-      cumulativePumpTrips: res.map((item) => item.cumulativePumpTrips || 0),
-      cumulativeMixSand: res.map((item) => item.cumulativeMixSand || 0),
-      utilizationRate: res.map((item) => Number(((item.utilizationRate || 0) * 100).toFixed(2)))
-    }
-
-    xAxisData.value = res.map((item) => item.reportDate || '')
-  } finally {
-    chartLoading.value = false
-  }
-}, 500)
-
-const resizer = () => {
-  chart?.resize()
-}
-
-onUnmounted(() => {
-  window.removeEventListener('resize', resizer)
-})
-
-const render = () => {
-  if (!chartRef.value) return
-
-  chart = echarts.init(chartRef.value, undefined, { renderer: 'canvas' })
-
-  window.addEventListener('resize', resizer)
-
-  const values: number[] = []
-
-  for (const [_name, key] of legend.value) {
-    values.push(...(chartData.value[key] || []))
-  }
-
-  const maxVal = values.length === 0 ? 10000 : Math.max(...values)
-  const minVal = values.length === 0 ? 0 : Math.min(...values) > 0 ? 0 : Math.min(...values)
-
-  const maxDigits = (Math.floor(maxVal) + '').length
-  const minDigits = minVal === 0 ? 0 : (Math.floor(Math.abs(minVal)) + '').length
-  const interval = Math.max(maxDigits, minDigits)
-
-  const maxInterval = interval
-  const minInterval = minDigits
-
-  const intervalArr = [0]
-  for (let i = 1; i <= interval; i++) {
-    intervalArr.push(Math.pow(10, i))
-  }
-
-  chart.setOption({
-    tooltip: {
-      trigger: 'axis',
-      axisPointer: {
-        type: 'line'
-      },
-      formatter: (params) => {
-        let d = `${params[0].axisValueLabel}<br>`
-        let item = params.map((el) => {
-          return `<div class="flex items-center justify-between mt-1 gap-1">
-            <span>${el.marker} ${el.seriesName}</span>
-            <span>${chartData.value[legend.value[el.componentIndex][1]][el.dataIndex].toFixed(2)} ${el.seriesName.split(' ')[1] ?? ''}</span>
-          </div>`
-        })
-
-        return d + item.join('')
-      }
-    },
-    legend: {
-      data: legend.value.map(([name]) => name),
-      show: true
-    },
-    xAxis: {
-      type: 'category',
-      data: xAxisData.value
-    },
-    yAxis: {
-      type: 'value',
-      min: -minInterval,
-      max: maxInterval,
-      interval: 1,
-      axisLabel: {
-        formatter: (v) => {
-          const num = v === 0 ? 0 : v > 0 ? Math.pow(10, v) : -Math.pow(10, -v)
-
-          return num.toLocaleString()
-        }
-      }
-    },
-    series: legend.value.map(([name, key]) => ({
-      name,
-      type: 'line',
-      smooth: true,
-      showSymbol: true,
-      data: chartData.value[key].map((value) => {
-        if (value === 0) return 0
-
-        const isPositive = value > 0
-        const absItem = Math.abs(value)
-
-        const min_value = Math.max(...intervalArr.filter((v) => v <= absItem))
-        const min_index = intervalArr.findIndex((v) => v === min_value)
-
-        const new_value =
-          (absItem - min_value) / (intervalArr[min_index + 1] - intervalArr[min_index]) + min_index
-
-        return isPositive ? new_value : -new_value
-      })
-    }))
-  })
-}
+const refreshKey = ref(0)
 
 const handleDeptNodeClick = (node: any) => {
   deptName.value = node.name
   handleQuery()
 }
 
-const handleQuery = (setPage = true) => {
-  if (setPage) {
-    query.value.pageNo = 1
-  }
-  getChart().then(() => {
-    render()
-  })
-  getList()
-  getTotal()
+const handleQuery = () => {
+  query.value.pageNo = 1
+  refreshKey.value += 1
 }
 
 const resetQuery = () => {
-  query.value = {
-    pageNo: 1,
-    pageSize: 10,
-    deptId: deptId,
-    contractName: '',
-    taskName: '',
-    createTime: [
-      ...rangeShortcuts[1].value().map((item) => dayjs(item).format('YYYY-MM-DD HH:mm:ss'))
-    ]
-  }
+  query.value = createDefaultQuery()
+  deptName.value = '四川瑞都'
   handleQuery()
 }
-
-watch(
-  () => query.value.createTime,
-  () => {
-    handleQuery(false)
-  }
-)
-
-watch([() => query.value.contractName, () => query.value.taskName], () => {
-  handleQuery(false)
-})
-
-onMounted(() => {
-  handleQuery()
-})
-
-const exportChart = () => {
-  if (!chart) return
-  let img = new Image()
-  img.src = chart.getDataURL({
-    type: 'png',
-    pixelRatio: 1,
-    backgroundColor: '#fff'
-  })
-
-  img.onload = function () {
-    let canvas = document.createElement('canvas')
-    canvas.width = img.width
-    canvas.height = img.height
-    let ctx = canvas.getContext('2d')
-    ctx?.drawImage(img, 0, 0)
-    let dataURL = canvas.toDataURL('image/png')
-
-    let a = document.createElement('a')
-
-    let event = new MouseEvent('click')
-
-    a.href = dataURL
-    a.download = `瑞恒日报统计数据.png`
-    a.dispatchEvent(event)
-  }
-}
-
-const exportData = async () => {
-  const res = await IotRdDailyReportApi.exportRdDailyReportStatistics(query.value)
-  download.excel(res, '瑞都日报统计数据.xlsx')
-}
-
-const exportAll = async () => {
-  if (tab.value === '看板') exportChart()
-  else exportData()
-}
-
-const router = useRouter()
-
-const tolist = (id: number) => {
-  const { pageNo, pageSize, ...rest } = query.value
-
-  router.push({
-    path: '/iotdayilyreport/IotRdDailyReport',
-    query: {
-      ...rest,
-      deptId: id
-    }
-  })
-}
-
-const { ZmTable, ZmTableColumn } = useTableComponents()
 </script>
 
 <template>
   <div
-    class="grid grid-cols-[auto_1fr] grid-rows-[62px_128px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+    class="grid grid-cols-[auto_1fr] grid-rows-[62px_48px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
   >
     <DeptTreeSelect
       :deptId="id"
@@ -468,9 +61,6 @@ const { ZmTable, ZmTableColumn } = useTableComponents()
       v-model="query.deptId"
       @node-click="handleDeptNodeClick"
     />
-    <!-- <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg row-span-3">
-
-    </div> -->
     <el-form
       size="default"
       class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"
@@ -514,116 +104,36 @@ const { ZmTable, ZmTableColumn } = useTableComponents()
         <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
       </el-form-item>
     </el-form>
-    <div class="grid grid-cols-9 gap-8">
-      <div
-        v-for="info in totalWorkKeys"
-        :key="info[0]"
-        class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow p-1 flex flex-col items-center justify-center gap-1"
-      >
-        <div class="size-7.5" :class="info[3]"></div>
-        <count-to
-          class="text-2xl font-medium"
-          :start-val="0"
-          :end-val="totalWork[info[0]]"
-          :decimals="info[4]"
-        >
-          <span class="text-xs leading-8 text-[var(--el-text-color-regular)]">暂无数据</span>
-        </count-to>
-        <div class="text-sm font-medium text-[var(--el-text-color-regular)] whitespace-nowrap">
-          {{ info[1] ? info[2] + '(' + info[1] + ')' : info[2] }}
-        </div>
-      </div>
-    </div>
 
-    <div class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow flex flex-col p-4 gap-2">
-      <div class="flex h-12 items-center justify-between">
-        <el-button-group>
-          <el-button
-            size="default"
-            :type="tab === '表格' ? 'primary' : 'default'"
-            @click="handleSelectTab('表格')"
-            >表格
-          </el-button>
-          <el-button
-            size="default"
-            :type="tab === '看板' ? 'primary' : 'default'"
-            @click="handleSelectTab('看板')"
-            >看板
-          </el-button>
-        </el-button-group>
-        <h3 class="text-xl font-medium">{{ `${deptName}-${tab}` }}</h3>
-        <el-button size="default" type="primary" @click="exportAll">导出</el-button>
-      </div>
-      <div class="flex-1 relative">
-        <el-auto-resizer class="absolute">
-          <template #default="{ height }">
-            <Motion
-              as="div"
-              :style="{ position: 'relative', overflow: 'hidden' }"
-              :animate="{ height: `${height}px`, width: `100%` }"
-              :transition="{ type: 'spring', stiffness: 200, damping: 25, duration: 0.3 }"
-            >
-              <AnimatePresence :initial="false" mode="sync">
-                <Motion
-                  :key="currentTab"
-                  as="div"
-                  :initial="{ x: direction === 'left' ? '-100%' : '100%', opacity: 0 }"
-                  :animate="{ x: '0%', opacity: 1 }"
-                  :exit="{ x: direction === 'left' ? '50%' : '-50%', opacity: 0 }"
-                  :transition="{ type: 'tween', stiffness: 300, damping: 30, duration: 0.3 }"
-                  :style="{ position: 'absolute', left: 0, right: 0, top: 0 }"
-                >
-                  <div :style="{ width: `100%`, height: `${height}px` }">
-                    <zm-table
-                      v-if="currentTab === '表格'"
-                      :loading="listLoading"
-                      :data="list"
-                      :height="height"
-                      show-border
-                    >
-                      <zm-table-column label="部门" prop="name">
-                        <template #default="{ row }">
-                          <el-button text type="primary" @click.prevent="tolist(row.id)">{{
-                            row.name
-                          }}</el-button>
-                        </template>
-                      </zm-table-column>
-                      <zm-table-column label="油耗(万升)" prop="totalDailyFuel" />
-                      <zm-table-column label="桥塞" prop="cumulativeBridgePlug" />
-                      <zm-table-column label="趟数" prop="cumulativeRunCount" />
-                      <zm-table-column label="井数" prop="cumulativeWorkingWell" />
-                      <zm-table-column label="段数" prop="cumulativeWorkingLayers" />
-                      <zm-table-column label="小时(H)" prop="cumulativeHourCount" />
-                      <zm-table-column label="水方量(方)" prop="cumulativeWaterVolume" />
-                      <zm-table-column label="台次">
-                        <zm-table-column label="泵车" prop="cumulativePumpTrips" />
-                        <zm-table-column label="仪表/混砂" prop="cumulativeMixSand" />
-                      </zm-table-column>
-                      <zm-table-column
-                        label="设备利用率"
-                        prop="utilizationRate"
-                        cover-formatter
-                        :real-value="
-                          (row: List) => (Number(row.utilizationRate ?? 0) * 100).toFixed(2) + '%'
-                        "
-                      />
-                    </zm-table>
-                    <div
-                      ref="chartRef"
-                      v-loading="chartLoading"
-                      :key="dayjs().valueOf()"
-                      v-else
-                      :style="{ width: `100%`, height: `${height}px` }"
-                    >
-                    </div>
-                  </div>
-                </Motion>
-              </AnimatePresence>
-            </Motion>
-          </template>
-        </el-auto-resizer>
-      </div>
-    </div>
+    <el-button-group class="justify-self-start self-center">
+      <el-button
+        size="default"
+        :type="activeTab === '日报统计' ? 'primary' : 'default'"
+        @click="activeTab = '日报统计'"
+      >
+        日报统计
+      </el-button>
+      <el-button
+        size="default"
+        :type="activeTab === '非生产时效' ? 'primary' : 'default'"
+        @click="activeTab = '非生产时效'"
+      >
+        非生产时效
+      </el-button>
+    </el-button-group>
+
+    <DailyStatistics
+      v-if="activeTab === '日报统计'"
+      :query="query"
+      :dept-name="deptName"
+      :refresh-key="refreshKey"
+    />
+    <NonProductionEfficiency
+      v-else
+      :query="query"
+      :dept-name="deptName"
+      :refresh-key="refreshKey"
+    />
   </div>
 </template>