yanghao пре 1 недеља
родитељ
комит
8280755c15

+ 1 - 0
src/api/pms/iotrddailyreport/index.ts

@@ -2,6 +2,7 @@ import request from '@/config/axios'
 
 // 瑞都日报 VO
 export interface IotRdDailyReportVO {
+  createTime:any,
   id: number // 主键id
   deptId: number // 施工队伍id
   projectId: number // 项目id

+ 7 - 2
src/api/pms/iotrhdailyreport/index.ts

@@ -2,6 +2,7 @@ import request from '@/config/axios'
 
 // 瑞恒日报 VO
 export interface IotRhDailyReportVO {
+  createTime: number
   id: number // 主键id
   deptId: number // 施工队伍id
   projectId: number // 项目id
@@ -23,7 +24,7 @@ export interface IotRhDailyReportVO {
   nextPlan: string // 下步工作计划
   constructionStatus: number // 施工状态(动迁 准备 施工 完工)
   personnel: string // 人员情况
-  capacity: number  // 产能
+  capacity: number // 产能
   totalGasInjection: number // 累计注气量(万方)
   totalWaterInjection: number // 累计注水量(方)
   cumulativeCompletion: number // 累计完工井次
@@ -42,6 +43,10 @@ export const IotRhDailyReportApi = {
     return await request.get({ url: `/pms/iot-rh-daily-report/page`, params })
   },
 
+  getIotRhDailyReportSummary: async (params: any) => {
+    return await request.get({ url: `/pms/iot-rh-daily-report/statistics`, params })
+  },
+
   // 累计工作量统计
   totalWorkload: async (params: any) => {
     return await request.get({ url: `/pms/iot-rh-daily-report/totalWorkload`, params })
@@ -85,5 +90,5 @@ export const IotRhDailyReportApi = {
   // 导出瑞恒日报 Excel
   exportIotRhDailyReport: async (params) => {
     return await request.download({ url: `/pms/iot-rh-daily-report/export-excel`, params })
-  },
+  }
 }

+ 22 - 4
src/api/pms/iotrydailyreport/index.ts

@@ -2,6 +2,10 @@ import request from '@/config/axios'
 
 // 瑞鹰日报 VO
 export interface IotRyDailyReportVO {
+  createTime: number
+  transitTime: any
+  totalStaffNum: any
+  offDutyStaffNum: any
   id: number // 主键id
   deptId: number // 施工队伍id
   projectId: number // 项目id
@@ -26,9 +30,9 @@ export interface IotRyDailyReportVO {
   selfStopTime: number
   complexityTime: number
   relocationTime: number
-  rectificationTime:  number
-  waitingStopTime:  number
-  winterBreakTime:  number
+  rectificationTime: number
+  waitingStopTime: number
+  winterBreakTime: number
   constructionStartDate: Date // 施工开始日期
   constructionEndDate: Date // 施工结束日期
   productionStatus: string // 当日生产情况生产动态
@@ -55,6 +59,20 @@ export const IotRyDailyReportApi = {
     return await request.get({ url: `/pms/iot-ry-daily-report/page`, params })
   },
 
+  getIotRyDailyReportSummary: async (params: any) => {
+    return await request.get({ url: `/pms/iot-ry-daily-report/statistics`, params })
+  },
+
+  // 累计工作量统计
+  totalWorkload: async (params: any) => {
+    return await request.get({ url: `/pms/iot-ry-daily-report/totalWorkload`, params })
+  },
+
+  // 按照日期查询瑞恒日报统计数据 已填报 未填报 数量
+  ryDailyReportStatistics: async (params: any) => {
+    return await request.get({ url: `/pms/iot-ry-daily-report/ryDailyReportStatistics`, params })
+  },
+
   // 查询瑞鹰日报详情
   getIotRyDailyReport: async (id: number) => {
     return await request.get({ url: `/pms/iot-ry-daily-report/get?id=` + id })
@@ -78,5 +96,5 @@ export const IotRyDailyReportApi = {
   // 导出瑞鹰日报 Excel
   exportIotRyDailyReport: async (params) => {
     return await request.download({ url: `/pms/iot-ry-daily-report/export-excel`, params })
-  },
+  }
 }

+ 94 - 0
src/components/count-to1.vue

@@ -0,0 +1,94 @@
+<script setup lang="ts">
+import { TransitionPresets, useTransition } from '@vueuse/core'
+
+defineOptions({
+  name: 'FaCountTo'
+})
+
+const props = withDefaults(
+  defineProps<{
+    startVal: number
+    endVal: number
+    autoplay?: boolean
+    duration?: number
+    transition?: keyof typeof TransitionPresets
+    delay?: number
+    decimals?: number
+    separator?: string
+    prefix?: string
+    suffix?: string
+  }>(),
+  {
+    autoplay: true,
+    duration: 2000,
+    transition: 'linear',
+    delay: 0,
+    decimals: 0,
+    separator: ','
+  }
+)
+
+const emits = defineEmits<{
+  onStarted: []
+  onFinished: []
+}>()
+
+const disabled = ref(false)
+
+const source = ref(props.startVal)
+const outputValue = useTransition(source, {
+  duration: props.duration,
+  transition: TransitionPresets[props.transition],
+  delay: props.delay,
+  disabled,
+  onStarted: () => emits('onStarted'),
+  onFinished: () => emits('onFinished')
+})
+const value = computed(() => {
+  let val: number | string = unref(outputValue.value)
+  val = Number(val).toFixed(props.decimals)
+  if (props.separator) {
+    const [integer, decimal] = val.toString().split('.')
+    val = integer.replace(/\B(?=(\d{3})+(?!\d))/g, props.separator) + (decimal ? `.${decimal}` : '')
+  }
+  if (props.prefix) {
+    val = props.prefix + val
+  }
+  if (props.suffix) {
+    val = val + props.suffix
+  }
+  return val
+})
+
+function start() {
+  source.value = props.endVal
+}
+
+function reset() {
+  disabled.value = true
+  source.value = props.startVal
+  nextTick(() => {
+    disabled.value = false
+  })
+}
+
+watch([() => props.startVal, () => props.endVal], () => {
+  start()
+})
+
+onMounted(() => {
+  props.autoplay && start()
+})
+
+defineExpose({
+  start,
+  reset
+})
+</script>
+
+<template>
+  <span>
+    <span v-if="endVal">{{ value }}</span>
+    <slot v-else></slot>
+  </span>
+</template>

+ 1 - 1
src/router/modules/remaining.ts

@@ -532,7 +532,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
         }
       },
       {
-        path: 'tddevice/detail/:id/:ifInline/:carOnline/:time/:name/:code/:dept/:vehicle?',
+        path: 'tddevice/detail',
         component: () => import('@/views/pms/device/monitor/TdDeviceInfo.vue'),
         name: 'TdDeviceDetail',
         meta: {

+ 2 - 2
src/utils/formatTime.ts

@@ -291,7 +291,7 @@ export function formatPast2(ms: number): string {
  * @param column 字段
  * @param cellValue 字段值
  */
-export function dateFormatter(_row: any, _column: TableColumnCtx<any>, cellValue: any): string {
+export function dateFormatter(_row: any, _column: TableColumnCtx<any> | null, cellValue: any): string {
   return cellValue ? formatDate(cellValue) : ''
 }
 
@@ -302,7 +302,7 @@ export function dateFormatter(_row: any, _column: TableColumnCtx<any>, cellValue
  * @param column 字段
  * @param cellValue 字段值
  */
-export function dateFormatter2(_row: any, _column: TableColumnCtx<any>, cellValue: any): string {
+export function dateFormatter2(_row: any, _column: TableColumnCtx<any> |null, cellValue: any): string {
   return cellValue ? formatDate(cellValue, 'YYYY-MM-DD') : ''
 }
 

+ 15 - 14
src/views/pms/device/monitor/TdDeviceInfo.vue

@@ -12,16 +12,18 @@ defineOptions({ name: 'TdDeviceDetail' })
 
 dayjs.extend(quarterOfYear)
 
-const { params } = useRoute()
+const { query } = useRoute()
+
+console.log('query :>> ', query)
 
 const data = ref({
-  deviceCode: params.code || '',
-  deviceName: params.name || '',
-  lastInlineTime: params.time || '',
-  ifInline: params.ifInline || '',
-  dept: params.dept || '',
-  vehicle: params.vehicle || '',
-  carOnline: params.carOnline || ''
+  deviceCode: query.code || '',
+  deviceName: query.name || '',
+  lastInlineTime: query.time || '',
+  ifInline: query.ifInline || '',
+  dept: query.dept || '',
+  vehicle: query.vehicle || '',
+  carOnline: query.carOnline || ''
 })
 
 const disabledIdentifier = ref<string[]>(['online', 'vehicle_name', 'touchScreenDataAccumulate'])
@@ -100,10 +102,10 @@ let minInterval = 0
 
 // 1. 加载 specs
 const loadSpecs = async () => {
-  if (!params.id) return
+  if (!query.id) return
   specsLoading.value = true
-  const res = await IotDeviceApi.getIotDeviceTds(Number(params.id))
-  const zhbdres = await IotDeviceApi.getIotDeviceZHBDTds(Number(params.id))
+  const res = await IotDeviceApi.getIotDeviceTds(Number(query.id))
+  const zhbdres = await IotDeviceApi.getIotDeviceZHBDTds(Number(query.id))
 
   zhbdspecs.value = zhbdres.sort((a, b) => b.modelOrder - a.modelOrder)
   gatewayspecs.value = res.sort((a, b) => b.modelOrder - a.modelOrder)
@@ -244,8 +246,7 @@ const render = () => {
       axisLabel: {
         formatter: (v) => dayjs(v).format('YYYY-MM-DD HH:mm:ss'),
         rotate: 20
-      },
-      inverse: true
+      }
     },
     yAxis: {
       type: 'value',
@@ -365,7 +366,7 @@ onUnmounted(() => {
           离线
         </el-tag>
       </el-form-item>
-      <el-form-item label="中航北斗" class="online" type="plain">
+      <el-form-item v-if="data.carOnline" label="中航北斗" class="online" type="plain">
         <el-tag
           v-if="data.carOnline === 'true'"
           type="success"

+ 3 - 3
src/views/pms/device/monitor/index.vue

@@ -200,7 +200,7 @@
                             item.deviceCode,
                             item.deptName,
                             item.vehicleName,
-                            item.carOnline
+                            item.carOnline ?? ''
                           )
                         "
                       >
@@ -267,7 +267,7 @@
                       scope.row.deviceCode,
                       scope.row.deptName,
                       scope.row.vehicleName,
-                      scope.row.carOnline
+                      scope.row.carOnline ?? ''
                     )
                   "
                 >
@@ -394,7 +394,7 @@ const openDetail = (
   }
   push({
     name: 'TdDeviceDetail',
-    params: { id, ifInline, carOnline, time, name, code, dept, vehicle }
+    query: { id, ifInline, carOnline, time, name, code, dept, vehicle }
   })
 }
 

+ 119 - 10
src/views/pms/iotrddailyreport/fillDailyReport.vue

@@ -42,11 +42,16 @@
               end-placeholder="结束日期"
               :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
               class="!w-220px"
+              :shortcuts="rangeShortcuts"
             />
           </el-form-item>
           <el-form-item>
-            <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-            <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+            <el-button @click="handleQuery"
+              ><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button
+            >
+            <el-button @click="resetQuery"
+              ><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button
+            >
             <el-button
               type="primary"
               plain
@@ -70,8 +75,15 @@
 
       <!-- 列表 -->
       <ContentWrap>
-        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-          <el-table-column label="主键id" align="center" prop="id" v-if="false"/>
+        <el-table
+          v-loading="loading"
+          :data="list"
+          :stripe="true"
+          :style="{ width: '100%' }"
+          max-height="600"
+          show-overflow-tooltip
+          border
+        >
           <el-table-column label="操作" align="center" min-width="120px">
             <template #default="scope">
               <el-button
@@ -148,9 +160,106 @@
 import { dateFormatter } from '@/utils/formatTime'
 import download from '@/utils/download'
 import { IotRdDailyReportApi, IotRdDailyReportVO } from '@/api/pms/iotrddailyreport'
-import FillDailyReportForm from './FillDailyReportForm.vue'
-import {DICT_TYPE} from "@/utils/dict";
-import DeptTree2 from "@/views/pms/iotrhdailyreport/DeptTree2.vue";
+import { DICT_TYPE } from '@/utils/dict'
+import DeptTree2 from '@/views/pms/iotrhdailyreport/DeptTree2.vue'
+
+import dayjs from 'dayjs'
+import quarterOfYear from 'dayjs/plugin/quarterOfYear'
+
+dayjs.extend(quarterOfYear)
+
+const rangeShortcuts = [
+  {
+    text: '今天',
+    value: () => {
+      const today = dayjs()
+      return [today.startOf('day').toDate(), today.endOf('day').toDate()]
+    }
+  },
+  {
+    text: '昨天',
+    value: () => {
+      const yesterday = dayjs().subtract(1, 'day')
+      return [yesterday.startOf('day').toDate(), yesterday.endOf('day').toDate()]
+    }
+  },
+  {
+    text: '本周',
+    value: () => {
+      return [dayjs().startOf('week').toDate(), dayjs().endOf('week').toDate()]
+    }
+  },
+  {
+    text: '上周',
+    value: () => {
+      const lastWeek = dayjs().subtract(1, 'week')
+      return [lastWeek.startOf('week').toDate(), lastWeek.endOf('week').toDate()]
+    }
+  },
+  {
+    text: '本月',
+    value: () => {
+      return [dayjs().startOf('month').toDate(), dayjs().endOf('month').toDate()]
+    }
+  },
+  {
+    text: '上月',
+    value: () => {
+      const lastMonth = dayjs().subtract(1, 'month')
+      return [lastMonth.startOf('month').toDate(), lastMonth.endOf('month').toDate()]
+    }
+  },
+  {
+    text: '本季度',
+    value: () => {
+      return [dayjs().startOf('quarter').toDate(), dayjs().endOf('quarter').toDate()]
+    }
+  },
+  {
+    text: '上季度',
+    value: () => {
+      const lastQuarter = dayjs().subtract(1, 'quarter')
+      return [lastQuarter.startOf('quarter').toDate(), lastQuarter.endOf('quarter').toDate()]
+    }
+  },
+  {
+    text: '今年',
+    value: () => {
+      return [dayjs().startOf('year').toDate(), dayjs().endOf('year').toDate()]
+    }
+  },
+  {
+    text: '去年',
+    value: () => {
+      const lastYear = dayjs().subtract(1, 'year')
+      return [lastYear.startOf('year').toDate(), lastYear.endOf('year').toDate()]
+    }
+  },
+  {
+    text: '最近7天',
+    value: () => {
+      return [dayjs().subtract(6, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近30天',
+    value: () => {
+      return [dayjs().subtract(29, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近90天',
+    value: () => {
+      return [dayjs().subtract(89, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近一年',
+    value: () => {
+      return [dayjs().subtract(1, 'year').toDate(), dayjs().toDate()]
+    }
+  }
+]
 
 /** 瑞都日报 填报 列表 */
 defineOptions({ name: 'FillDailyReport' })
@@ -207,7 +316,7 @@ const queryParams = reactive({
   status: undefined,
   processInstanceId: undefined,
   auditStatus: undefined,
-  createTime: [],
+  createTime: []
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
@@ -249,7 +358,7 @@ const resetQuery = () => {
 /** 添加/修改操作 */
 const formRef = ref()
 const openForm = (type: string, id?: number) => {
-  push({ name: 'FillDailyReportForm', params:{ id: id, mode: 'fill' } })
+  push({ name: 'FillDailyReportForm', params: { id: id, mode: 'fill' } })
 }
 
 /** 删除按钮操作 */
@@ -273,7 +382,7 @@ const handleDetail = async (id: number) => {
       name: 'FillDailyReportForm',
       params: {
         id: id.toString(),
-        mode: 'detail'  // 添加详情模式标识
+        mode: 'detail' // 添加详情模式标识
       }
     })
   } catch (error) {

+ 452 - 210
src/views/pms/iotrddailyreport/index.vue

@@ -42,11 +42,16 @@
               end-placeholder="结束日期"
               :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
               class="!w-220px"
+              :shortcuts="rangeShortcuts"
             />
           </el-form-item>
           <el-form-item>
-            <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-            <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+            <el-button @click="handleQuery"
+              ><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button
+            >
+            <el-button @click="resetQuery"
+              ><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button
+            >
             <el-button
               type="primary"
               plain
@@ -70,25 +75,78 @@
 
       <!-- 列表 -->
       <ContentWrap ref="tableContainerRef">
-        <el-table ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-          <el-table-column label="主键id" align="center" prop="id" v-if="false"/>
+        <el-table
+          ref="tableRef"
+          v-loading="loading"
+          :data="list"
+          :stripe="true"
+          :style="{ width: '100%' }"
+          max-height="600"
+          show-overflow-tooltip
+          border
+        >
           <el-table-column
             label="创建时间"
             align="center"
             prop="createTime"
-            :formatter="dateFormatter2"
-            :width="columnWidths.createTime"
+            :formatter="dateFormatter"
+            :min-width="columnWidths.createTime.width"
+            resizable
+          />
+          <el-table-column
+            label="施工队伍"
+            align="center"
+            prop="deptName"
+            :min-width="columnWidths.deptName.width"
+            resizable
+          />
+          <el-table-column
+            label="项目"
+            align="center"
+            prop="contractName"
+            :min-width="columnWidths.contractName.width"
+            resizable
           />
-          <el-table-column label="施工队伍" align="center" prop="deptName" :width="columnWidths.deptName"/>
-          <el-table-column label="任务" align="center" prop="taskName" :width="columnWidths.taskName"/>
-          <el-table-column :label="t('project.status')" align="center" prop="rdStatus" :width="columnWidths.rdStatus">
+          <el-table-column
+            label="任务"
+            align="center"
+            prop="taskName"
+            :min-width="columnWidths.taskName.width"
+            resizable
+          />
+          <el-table-column
+            label="时间节点"
+            align="center"
+            prop="timeRange"
+            :min-width="columnWidths.timeRange.width"
+            resizable
+          />
+          <el-table-column
+            :label="t('project.status')"
+            align="center"
+            prop="rdStatus"
+            :min-width="columnWidths.rdStatus.width"
+            resizable
+          >
             <template #default="scope">
               <dict-tag :type="DICT_TYPE.PMS_PROJECT_RD_STATUS" :value="scope.row.rdStatus" />
             </template>
           </el-table-column>
-          <el-table-column label="项目" align="center" prop="contractName" :width="columnWidths.contractName" v-if="false"/>
+          <el-table-column
+            label="项目"
+            align="center"
+            prop="contractName"
+            :width="columnWidths.contractName"
+            v-if="false"
+          />
 
-          <el-table-column label="时间节点" align="center" prop="timeRange" :width="columnWidths.timeRange" v-if="false"/>
+          <el-table-column
+            label="时间节点"
+            align="center"
+            prop="timeRange"
+            :width="columnWidths.timeRange"
+            v-if="false"
+          />
 
           <!--
           <el-table-column label="项目类别(钻井 修井 注氮 酸化压裂... )" align="center" prop="projectClassification" />
@@ -97,43 +155,114 @@
           <el-table-column label="施工设备" align="center" prop="deviceIds" /> -->
           <!--
           <el-table-column label="时间节点-结束" align="center" prop="endTime" /> -->
-          <el-table-column label="当日施工井" align="center" prop="cumulativeWorkingWell" :width="columnWidths.cumulativeWorkingWell"/>
-          <el-table-column label="当日施工层" align="center" prop="cumulativeWorkingLayers" :width="columnWidths.cumulativeWorkingLayers"/>
-          <el-table-column label="当日泵车台次" align="center" prop="dailyPumpTrips" :width="columnWidths.dailyPumpTrips"/>
-          <el-table-column label="当日仪表/混砂" align="center" prop="dailyToolsSand" :width="columnWidths.dailyToolsSand"/>
-          <el-table-column label="趟数" align="center" prop="runCount" :width="columnWidths.runCount"/>
-          <el-table-column label="桥塞" align="center" prop="bridgePlug" :width="columnWidths.bridgePlug"/>
-          <el-table-column label="水方量" align="center" prop="waterVolume" :width="columnWidths.waterVolume"/>
-          <el-table-column label="时间H" align="center" prop="hourCount" :width="columnWidths.hourCount"/>
-          <el-table-column label="当日生产动态" align="center" :width="columnWidths.productionStatus" fixed-width>
-            <template #default="scope">
-              <el-tooltip
-                effect="light"
-                :content="scope.row.productionStatus"
-                placement="top"
-                popper-class="long-text-tooltip"
-                :disabled="!scope.row.productionStatus || scope.row.productionStatus.length <= 30"
-              >
-                <span class="long-text">{{ formatLongText(scope.row.productionStatus) }}</span>
-              </el-tooltip>
-            </template>
-          </el-table-column>
-          <el-table-column label="下步工作计划" align="center" :width="columnWidths.nextPlan" fixed-width>
-            <template #default="scope">
-              <el-tooltip
-                effect="light"
-                :content="scope.row.nextPlan"
-                placement="top"
-                popper-class="long-text-tooltip"
-                :disabled="!scope.row.nextPlan || scope.row.nextPlan.length <= 30"
-              >
-                <span class="long-text">{{ formatLongText(scope.row.nextPlan) }}</span>
-              </el-tooltip>
-            </template>
+          <el-table-column align="center" label="当日">
+            <el-table-column
+              label="施工井"
+              align="center"
+              prop="cumulativeWorkingWell"
+              :min-width="columnWidths.cumulativeWorkingWell.width"
+              resizable
+            />
+            <el-table-column
+              label="施工层"
+              align="center"
+              prop="cumulativeWorkingLayers"
+              :min-width="columnWidths.cumulativeWorkingLayers.width"
+              resizable
+            />
+            <el-table-column
+              label="泵车台次"
+              align="center"
+              prop="dailyPumpTrips"
+              :min-width="columnWidths.dailyPumpTrips.width"
+              resizable
+            />
+            <el-table-column
+              label="仪表/混砂"
+              align="center"
+              prop="dailyToolsSand"
+              :min-width="columnWidths.dailyToolsSand.width"
+              resizable
+            />
           </el-table-column>
-          <el-table-column label="外租设备" align="center" prop="externalRental" :width="columnWidths.externalRental"/>
-          <el-table-column label="故障情况" align="center" prop="malfunction" :width="columnWidths.malfunction" v-if="false"/>
-          <el-table-column label="故障误工H" align="center" prop="faultDowntime" :width="columnWidths.faultDowntime" v-if="false"/>
+          <el-table-column
+            label="趟数"
+            align="center"
+            prop="runCount"
+            :min-width="columnWidths.runCount.width"
+            resizable
+          />
+          <el-table-column
+            label="桥塞"
+            align="center"
+            prop="bridgePlug"
+            :min-width="columnWidths.bridgePlug.width"
+            resizable
+          />
+          <el-table-column
+            label="水方量"
+            align="center"
+            prop="waterVolume"
+            :min-width="columnWidths.waterVolume.width"
+            resizable
+          />
+          <el-table-column
+            label="时间H"
+            align="center"
+            prop="hourCount"
+            :min-width="columnWidths.hourCount.width"
+            resizable
+          />
+          <!--
+          <el-table-column
+            label="施工开始日期"
+            align="center"
+            prop="constructionStartDate"
+            :formatter="dateFormatter"
+            :min-width="columnWidths.constructionStartDate"
+          />
+          <el-table-column
+            label="施工结束日期"
+            align="center"
+            prop="constructionEndDate"
+            :formatter="dateFormatter"
+            :min-width="columnWidths.constructionEndDate"
+          /> -->
+          <el-table-column
+            label="当日生产动态"
+            align="center"
+            prop="productionStatus"
+            :min-width="columnWidths.productionStatus.width"
+            resizable
+          />
+          <el-table-column
+            label="下步工作计划"
+            align="center"
+            prop="nextPlan"
+            :min-width="columnWidths.nextPlan.width"
+            fixed-width
+          />
+          <el-table-column
+            label="外租设备"
+            align="center"
+            prop="externalRental"
+            :min-width="columnWidths.externalRental.width"
+            resizable
+          />
+          <el-table-column
+            label="故障情况"
+            align="center"
+            prop="malfunction"
+            :min-width="columnWidths.malfunction.width"
+            resizable
+          />
+          <el-table-column
+            label="故障误工H"
+            align="center"
+            prop="faultDowntime"
+            :min-width="columnWidths.faultDowntime.width"
+            resizable
+          />
 
           <el-table-column label="操作" align="center" min-width="120px" fixed="right">
             <template #default="scope">
@@ -170,18 +299,115 @@
       <IotRdDailyReportForm ref="formRef" @success="getList" />
     </el-col>
   </el-row>
-
 </template>
 
 <script setup lang="ts">
-import {dateFormatter, dateFormatter2} from '@/utils/formatTime'
+import { dateFormatter } from '@/utils/formatTime'
 import download from '@/utils/download'
 import { IotRdDailyReportApi, IotRdDailyReportVO } from '@/api/pms/iotrddailyreport'
 import IotRdDailyReportForm from './IotRdDailyReportForm.vue'
-import {DICT_TYPE} from "@/utils/dict";
+import { DICT_TYPE } from '@/utils/dict'
 import { useRoute } from 'vue-router'
-import DeptTree2 from "@/views/pms/iotrhdailyreport/DeptTree2.vue";
+import DeptTree2 from '@/views/pms/iotrhdailyreport/DeptTree2.vue'
+import { useDebounceFn } from '@vueuse/core'
 
+import dayjs from 'dayjs'
+import quarterOfYear from 'dayjs/plugin/quarterOfYear'
+
+dayjs.extend(quarterOfYear)
+
+const rangeShortcuts = [
+  {
+    text: '今天',
+    value: () => {
+      const today = dayjs()
+      return [today.startOf('day').toDate(), today.endOf('day').toDate()]
+    }
+  },
+  {
+    text: '昨天',
+    value: () => {
+      const yesterday = dayjs().subtract(1, 'day')
+      return [yesterday.startOf('day').toDate(), yesterday.endOf('day').toDate()]
+    }
+  },
+  {
+    text: '本周',
+    value: () => {
+      return [dayjs().startOf('week').toDate(), dayjs().endOf('week').toDate()]
+    }
+  },
+  {
+    text: '上周',
+    value: () => {
+      const lastWeek = dayjs().subtract(1, 'week')
+      return [lastWeek.startOf('week').toDate(), lastWeek.endOf('week').toDate()]
+    }
+  },
+  {
+    text: '本月',
+    value: () => {
+      return [dayjs().startOf('month').toDate(), dayjs().endOf('month').toDate()]
+    }
+  },
+  {
+    text: '上月',
+    value: () => {
+      const lastMonth = dayjs().subtract(1, 'month')
+      return [lastMonth.startOf('month').toDate(), lastMonth.endOf('month').toDate()]
+    }
+  },
+  {
+    text: '本季度',
+    value: () => {
+      return [dayjs().startOf('quarter').toDate(), dayjs().endOf('quarter').toDate()]
+    }
+  },
+  {
+    text: '上季度',
+    value: () => {
+      const lastQuarter = dayjs().subtract(1, 'quarter')
+      return [lastQuarter.startOf('quarter').toDate(), lastQuarter.endOf('quarter').toDate()]
+    }
+  },
+  {
+    text: '今年',
+    value: () => {
+      return [dayjs().startOf('year').toDate(), dayjs().endOf('year').toDate()]
+    }
+  },
+  {
+    text: '去年',
+    value: () => {
+      const lastYear = dayjs().subtract(1, 'year')
+      return [lastYear.startOf('year').toDate(), lastYear.endOf('year').toDate()]
+    }
+  },
+  {
+    text: '最近7天',
+    value: () => {
+      return [dayjs().subtract(6, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近30天',
+    value: () => {
+      return [dayjs().subtract(29, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近90天',
+    value: () => {
+      return [dayjs().subtract(89, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近一年',
+    value: () => {
+      return [dayjs().subtract(1, 'year').toDate(), dayjs().toDate()]
+    }
+  }
+]
 
 /** 瑞都日报 列表 */
 defineOptions({ name: 'IotRdDailyReport' })
@@ -238,7 +464,7 @@ const queryParams = reactive({
   status: undefined,
   processInstanceId: undefined,
   auditStatus: undefined,
-  createTime: [],
+  createTime: []
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
@@ -250,139 +476,148 @@ const tableRef = ref()
 // 表格容器引用
 const tableContainerRef = ref()
 
-// 列宽度配置
-const columnWidths = ref({
-  id: '80px',
-  deptName: '120px',
-  contractName: '120px',
-  taskName: '120px',
-  timeRange: '120px',
-  rdStatus: '120px',
-  cumulativeWorkingWell: '120px',
-  cumulativeWorkingLayers: '120px',
-  dailyPumpTrips: '120px',
-  dailyToolsSand: '120px',
-  runCount: '80px',
-  bridgePlug: '80px',
-  waterVolume: '100px',
-  hourCount: '80px',
-  constructionStartDate: '180px',
-  constructionEndDate: '180px',
-  productionStatus: '200px',
-  nextPlan: '200px',
-  externalRental: '120px',
-  malfunction: '150px',
-  faultDowntime: '120px',
-  createTime: '180px',
-  operation: '120px'
+const columnWidths = ref<
+  Record<
+    string,
+    { label: string; prop: string; width: string; fn?: (row: IotRdDailyReportVO) => string }
+  >
+>({
+  createTime: {
+    label: '创建时间',
+    prop: 'createTime',
+    width: '120px',
+    fn: (row: IotRdDailyReportVO) => dateFormatter(null, null, row.createTime)
+  },
+  deptName: {
+    label: '施工队伍',
+    prop: 'deptName',
+    width: '120px'
+  },
+  contractName: {
+    label: '项目',
+    prop: 'contractName',
+    width: '120px'
+  },
+  taskName: {
+    label: '任务',
+    prop: 'taskName',
+    width: '120px'
+  },
+  timeRange: {
+    label: '时间节点',
+    prop: 'timeRange',
+    width: '120px'
+  },
+  rdStatus: {
+    label: '施工状态',
+    prop: 'rdStatus',
+    width: '120px'
+  },
+  cumulativeWorkingWell: {
+    label: '施工井',
+    prop: 'cumulativeWorkingWell',
+    width: '120px'
+  },
+  cumulativeWorkingLayers: {
+    label: '施工层',
+    prop: 'cumulativeWorkingLayers',
+    width: '120px'
+  },
+  dailyPumpTrips: {
+    label: '泵车台次',
+    prop: 'dailyPumpTrips',
+    width: '120px'
+  },
+  dailyToolsSand: {
+    label: '仪表/混砂',
+    prop: 'dailyToolsSand',
+    width: '120px'
+  },
+  runCount: {
+    label: '趟数',
+    prop: 'runCount',
+    width: '120px'
+  },
+  bridgePlug: {
+    label: '桥塞',
+    prop: 'bridgePlug',
+    width: '120px'
+  },
+  waterVolume: {
+    label: '水方量',
+    prop: 'waterVolume',
+    width: '120px'
+  },
+  hourCount: {
+    label: '时间H',
+    prop: 'hourCount',
+    width: '120px'
+  },
+  productionStatus: {
+    label: '当日生产动态',
+    prop: 'productionStatus',
+    width: '120px'
+  },
+  nextPlan: {
+    label: '下步工作计划',
+    prop: 'nextPlan',
+    width: '120px'
+  },
+  externalRental: {
+    label: '外租设备',
+    prop: 'externalRental',
+    width: '120px'
+  },
+  malfunction: {
+    label: '故障情况',
+    prop: 'malfunction',
+    width: '120px'
+  },
+  faultDowntime: {
+    label: '故障误工H',
+    prop: 'faultDowntime',
+    width: '120px'
+  }
 })
 
-// 添加长文本格式化函数
-const formatLongText = (text: string | null | undefined) => {
-  if (!text) return '-';
-  // 如果文本长度超过30个字符,显示前30个字符并添加省略号
-  return text.length > 30 ? text.substring(0, 30) + '...' : text;
-};
-
 // 计算文本宽度
-const getTextWidth = (text: string, fontSize = 14) => {
-  const span = document.createElement('span');
-  span.style.visibility = 'hidden';
-  span.style.position = 'absolute';
-  span.style.whiteSpace = 'nowrap';
-  span.style.fontSize = `${fontSize}px`;
-  span.style.fontFamily = 'inherit';
-  span.innerText = text;
-
-  document.body.appendChild(span);
-  const width = span.offsetWidth;
-  document.body.removeChild(span);
-
-  return width;
-};
-
-// 计算列宽度
-const calculateColumnWidths = () => {
-  const MIN_WIDTH = 80; // 最小列宽
-  const PADDING = 25; // 列内边距
-
-  // 确保表格容器存在
-  if (!tableContainerRef.value?.$el) return;
-
-  const newWidths: Record<string, string> = {};
-
-  // 计算各列宽度的函数
-  const calculateColumnWidth = (key: string, label: string, getValue: Function) => {
-    // 如果是需要固定宽度的列,跳过计算
-    if (key === 'productionStatus' || key === 'nextPlan') {
-      return;
-    }
-
-    const headerWidth = getTextWidth(label) + PADDING;
-    let contentMaxWidth = MIN_WIDTH;
-
-    // 计算内容最大宽度
-    list.value.forEach((row, index) => {
-      let text = '';
-      if (key === 'rdStatus') {
-        // 特殊处理字典列,这里简化处理,实际应该获取字典标签
-        text = String(row[key] || '');
-      } else if (key.includes('Date') || key === 'createTime') {
-        // 日期列使用格式化后的值
-        text = dateFormatter(null, null, row[key]) || '';
-      } else {
-        text = String(getValue ? getValue(row, index) : (row[key] || ''));
-      }
+const getTextWidth = (text: string, fontSize = 12) => {
+  const span = document.createElement('span')
+  span.style.visibility = 'hidden'
+  span.style.position = 'absolute'
+  span.style.whiteSpace = 'nowrap'
+  span.style.fontSize = `${fontSize}px`
+  span.style.fontFamily = 'PingFang SC'
+  span.innerText = text
+
+  document.body.appendChild(span)
+  const width = span.offsetWidth
+  document.body.removeChild(span)
+
+  return width
+}
 
-      const textWidth = getTextWidth(text) + PADDING;
-      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth;
-    });
-
-    // 取标题宽度和内容最大宽度的较大值
-    const finalWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH);
-    newWidths[key] = `${finalWidth}px`;
-  };
-
-  // 计算各列宽度
-  calculateColumnWidth('deptName', '施工队伍', (row: any) => row.deptName);
-  calculateColumnWidth('contractName', '项目', (row: any) => row.contractName);
-  calculateColumnWidth('taskName', '任务', (row: any) => row.taskName);
-  calculateColumnWidth('timeRange', '时间节点', (row: any) => row.timeRange);
-  calculateColumnWidth('rdStatus', t('project.status'), (row: any) => row.rdStatus);
-  calculateColumnWidth('cumulativeWorkingWell', '累计施工井', (row: any) => row.cumulativeWorkingWell);
-  calculateColumnWidth('cumulativeWorkingLayers', '累计施工层', (row: any) => row.cumulativeWorkingLayers);
-  calculateColumnWidth('dailyPumpTrips', '当日泵车台次', (row: any) => row.dailyPumpTrips);
-  calculateColumnWidth('dailyToolsSand', '当日仪表/混砂', (row: any) => row.dailyToolsSand);
-  calculateColumnWidth('runCount', '趟数', (row: any) => row.runCount);
-  calculateColumnWidth('bridgePlug', '桥塞', (row: any) => row.bridgePlug);
-  calculateColumnWidth('waterVolume', '水方量', (row: any) => row.waterVolume);
-  calculateColumnWidth('hourCount', '时间H', (row: any) => row.hourCount);
-  calculateColumnWidth('constructionStartDate', '施工开始日期', (row: any) => dateFormatter(null, null, row.constructionStartDate));
-  calculateColumnWidth('constructionEndDate', '施工结束日期', (row: any) => dateFormatter(null, null, row.constructionEndDate));
-  calculateColumnWidth('productionStatus', '当日生产动态', (row: any) => row.productionStatus);
-  calculateColumnWidth('nextPlan', '下步工作计划', (row: any) => row.nextPlan);
-  calculateColumnWidth('externalRental', '外租设备', (row: any) => row.externalRental);
-  calculateColumnWidth('malfunction', '故障情况', (row: any) => row.malfunction);
-  calculateColumnWidth('faultDowntime', '故障误工H', (row: any) => row.faultDowntime);
-  calculateColumnWidth('createTime', '创建时间', (row: any) => dateFormatter(null, null, row.createTime));
-
-  // 为固定宽度的列设置固定值
-  newWidths.productionStatus = '200px';
-  newWidths.nextPlan = '200px';
-
-  // 操作列固定宽度
-  newWidths.operation = '120px';
-  // id列固定宽度(虽然隐藏)
-  newWidths.id = '80px';
-
-  // 更新列宽配置
-  columnWidths.value = newWidths;
-
-  // 触发表格重新布局
-  nextTick(() => {
-    tableRef.value?.doLayout();
-  });
-};
+const calculateColumnWidths = useDebounceFn(() => {
+  if (!tableContainerRef.value?.$el) return
+  Object.values(columnWidths.value).forEach(({ fn, prop, label, width }) => {
+    width =
+      Math.min(
+        ...[
+          Math.max(
+            ...[
+              getTextWidth(label),
+              ...list.value.map((v) => {
+                return getTextWidth(fn ? fn(v) : v[prop])
+              })
+            ]
+          ) + (label === '施工状态' ? 35 : 20),
+          200
+        ]
+      ) + 'px'
+
+    columnWidths.value[prop].width = width
+  })
+}, 1000)
 
 /** 查询列表 */
 const getList = async () => {
@@ -393,8 +628,8 @@ const getList = async () => {
     total.value = data.total
     // 获取数据后计算列宽
     nextTick(() => {
-      calculateColumnWidths();
-    });
+      calculateColumnWidths()
+    })
   } finally {
     loading.value = false
   }
@@ -426,7 +661,7 @@ const handleDetail = async (id: number) => {
       name: 'FillDailyReportForm',
       params: {
         id: id.toString(),
-        mode: 'detail'  // 添加详情模式标识
+        mode: 'detail' // 添加详情模式标识
       }
     })
   } catch (error) {
@@ -452,7 +687,7 @@ const handleApprove = async (id: number) => {
       name: 'FillDailyReportForm',
       params: {
         id: id.toString(),
-        mode: 'approval'  // 添加审批模式标识
+        mode: 'approval' // 添加审批模式标识
       }
     })
   } catch (error) {
@@ -489,7 +724,7 @@ const handleExport = async () => {
 }
 
 // 声明 ResizeObserver 实例
-let resizeObserver: ResizeObserver | null = null;
+let resizeObserver: ResizeObserver | null = null
 
 /** 初始化 **/
 onMounted(() => {
@@ -505,60 +740,67 @@ onMounted(() => {
   if (tableContainerRef.value?.$el) {
     resizeObserver = new ResizeObserver(() => {
       // 使用防抖避免频繁触发
-      clearTimeout((window as any).resizeTimer);
-      (window as any).resizeTimer = setTimeout(() => {
-        calculateColumnWidths();
-      }, 100);
-    });
-    resizeObserver.observe(tableContainerRef.value.$el);
+      clearTimeout((window as any).resizeTimer)
+      ;(window as any).resizeTimer = setTimeout(() => {
+        calculateColumnWidths()
+      }, 100)
+    })
+    resizeObserver.observe(tableContainerRef.value.$el)
   }
 })
 
 onUnmounted(() => {
   // 清除 ResizeObserver
   if (resizeObserver && tableContainerRef.value?.$el) {
-    resizeObserver.unobserve(tableContainerRef.value.$el);
-    resizeObserver = null;
+    resizeObserver.unobserve(tableContainerRef.value.$el)
+    resizeObserver = null
   }
 
   // 清除定时器
   if ((window as any).resizeTimer) {
-    clearTimeout((window as any).resizeTimer);
+    clearTimeout((window as any).resizeTimer)
   }
 })
 
 // 监听列表数据变化重新计算列宽
-watch(list, () => {
-  nextTick(calculateColumnWidths)
-}, { deep: true })
+watch(
+  list,
+  () => {
+    nextTick(calculateColumnWidths)
+  },
+  { deep: true }
+)
 </script>
 
 <style scoped>
 /* 确保表格单元格内容不换行 */
-:deep(.el-table .cell) {
-white-space: nowrap;
-}
+
+/* :deep(.el-table .cell) {
+  white-space: nowrap;
+} */
 
 /* 确保表格列标题不换行 */
-:deep(.el-table th > .cell) {
-white-space: nowrap;
-}
+
+/* :deep(.el-table th > .cell) {
+  white-space: nowrap;
+} */
 
 /* 调整表格最小宽度,确保内容完全显示 */
 :deep(.el-table) {
-min-width: 100%;
+  min-width: 100%;
 }
 
 /* 长文本样式 - 多行显示并添加省略号 */
 .long-text {
   display: -webkit-box;
-  -webkit-line-clamp: 2;
-  -webkit-box-orient: vertical;
+  max-height: 3em; /* 两行文本的高度 */
   overflow: hidden;
-  text-overflow: ellipsis;
   line-height: 1.5;
-  max-height: 3em; /* 两行文本的高度 */
+  text-overflow: ellipsis;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 2;
 }
+
 /* 确保固定宽度列不参与自动调整 */
 :deep(.el-table__header-wrapper .el-table__cell.fixed-width),
 :deep(.el-table__body-wrapper .el-table__cell.fixed-width) {
@@ -570,8 +812,8 @@ min-width: 100%;
 <style>
 /* 长文本 tooltip 样式 - 保留换行符 */
 .long-text-tooltip {
-  white-space: pre-line;
   max-width: 500px;
   line-height: 1.5;
+  white-space: pre-line;
 }
 </style>

+ 239 - 87
src/views/pms/iotrddailyreport/statistics.vue

@@ -42,11 +42,16 @@
               end-placeholder="结束日期"
               :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
               class="!w-220px"
+              :shortcuts="rangeShortcuts"
             />
           </el-form-item>
           <el-form-item>
-            <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-            <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+            <el-button @click="handleQuery"
+              ><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button
+            >
+            <el-button @click="resetQuery"
+              ><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button
+            >
             <el-button
               type="primary"
               plain
@@ -70,17 +75,51 @@
 
       <!-- 列表 -->
       <ContentWrap ref="tableContainerRef">
-        <el-table ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-          <el-table-column label="施工状态" align="center" prop="rdStatus" :width="columnWidths.rdStatus">
+        <el-table
+          ref="tableRef"
+          v-loading="loading"
+          :data="list"
+          :stripe="true"
+          :style="{ width: '100%' }"
+          max-height="600"
+          show-overflow-tooltip
+          border
+        >
+          <el-table-column
+            label="施工状态"
+            align="center"
+            prop="rdStatus"
+            :width="columnWidths.rdStatus"
+          >
             <template #default="scope">
               <dict-tag :type="DICT_TYPE.PMS_PROJECT_RD_STATUS" :value="scope.row.rdStatus" />
             </template>
           </el-table-column>
-          <el-table-column label="施工周期(D)" align="center" prop="period" :width="columnWidths.projectDeptName"/>
-          <el-table-column label="项目部" align="center" prop="projectDeptName" :width="columnWidths.projectDeptName"/>
-          <el-table-column label="队伍" align="center" prop="deptName" :width="columnWidths.deptName"/>
-
-          <el-table-column label="井号" align="center" prop="wellName" :width="columnWidths.wellName">
+          <el-table-column
+            label="施工周期(D)"
+            align="center"
+            prop="period"
+            :width="columnWidths.projectDeptName"
+          />
+          <el-table-column
+            label="项目部"
+            align="center"
+            prop="projectDeptName"
+            :width="columnWidths.projectDeptName"
+          />
+          <el-table-column
+            label="队伍"
+            align="center"
+            prop="deptName"
+            :width="columnWidths.deptName"
+          />
+
+          <el-table-column
+            label="井号"
+            align="center"
+            prop="wellName"
+            :width="columnWidths.wellName"
+          >
             <template #default="scope">
               <el-link
                 type="primary"
@@ -91,8 +130,18 @@
               </el-link>
             </template>
           </el-table-column>
-          <el-table-column label="工艺" align="center" prop="techniques" :width="columnWidths.techniques"/>
-          <el-table-column label="总工作量" align="center" prop="workloadDesign" :width="columnWidths.workloadDesign"/>
+          <el-table-column
+            label="工艺"
+            align="center"
+            prop="techniques"
+            :width="columnWidths.techniques"
+          />
+          <el-table-column
+            label="总工作量"
+            align="center"
+            prop="workloadDesign"
+            :width="columnWidths.workloadDesign"
+          />
           <!-- 已完成工作量分组列 -->
           <el-table-column label="已完成工作量" align="center">
             <!-- 动态生成列 -->
@@ -109,7 +158,12 @@
               </template>
             </el-table-column>
           </el-table-column>
-          <el-table-column label="甲方" align="center" prop="manufactureName" :width="columnWidths.manufactureName"/>
+          <el-table-column
+            label="甲方"
+            align="center"
+            prop="manufactureName"
+            :width="columnWidths.manufactureName"
+          />
           <!--
           <el-table-column label="操作" align="center" min-width="120px" fixed="right">
             <template #default="scope">
@@ -146,7 +200,6 @@
       <IotRdDailyReportForm ref="formRef" @success="getList" />
     </el-col>
   </el-row>
-
 </template>
 
 <script setup lang="ts">
@@ -154,9 +207,107 @@ import { dateFormatter } from '@/utils/formatTime'
 import download from '@/utils/download'
 import { IotRdDailyReportApi, IotRdDailyReportVO } from '@/api/pms/iotrddailyreport'
 import IotRdDailyReportForm from './IotRdDailyReportForm.vue'
-import {DICT_TYPE, getDictLabel} from "@/utils/dict";
+import { DICT_TYPE, getDictLabel } from '@/utils/dict'
 import { ref, reactive, onMounted, computed } from 'vue'
-import DeptTree2 from "@/views/pms/iotrhdailyreport/DeptTree2.vue";
+import DeptTree2 from '@/views/pms/iotrhdailyreport/DeptTree2.vue'
+
+import dayjs from 'dayjs'
+import quarterOfYear from 'dayjs/plugin/quarterOfYear'
+
+dayjs.extend(quarterOfYear)
+
+const rangeShortcuts = [
+  {
+    text: '今天',
+    value: () => {
+      const today = dayjs()
+      return [today.startOf('day').toDate(), today.endOf('day').toDate()]
+    }
+  },
+  {
+    text: '昨天',
+    value: () => {
+      const yesterday = dayjs().subtract(1, 'day')
+      return [yesterday.startOf('day').toDate(), yesterday.endOf('day').toDate()]
+    }
+  },
+  {
+    text: '本周',
+    value: () => {
+      return [dayjs().startOf('week').toDate(), dayjs().endOf('week').toDate()]
+    }
+  },
+  {
+    text: '上周',
+    value: () => {
+      const lastWeek = dayjs().subtract(1, 'week')
+      return [lastWeek.startOf('week').toDate(), lastWeek.endOf('week').toDate()]
+    }
+  },
+  {
+    text: '本月',
+    value: () => {
+      return [dayjs().startOf('month').toDate(), dayjs().endOf('month').toDate()]
+    }
+  },
+  {
+    text: '上月',
+    value: () => {
+      const lastMonth = dayjs().subtract(1, 'month')
+      return [lastMonth.startOf('month').toDate(), lastMonth.endOf('month').toDate()]
+    }
+  },
+  {
+    text: '本季度',
+    value: () => {
+      return [dayjs().startOf('quarter').toDate(), dayjs().endOf('quarter').toDate()]
+    }
+  },
+  {
+    text: '上季度',
+    value: () => {
+      const lastQuarter = dayjs().subtract(1, 'quarter')
+      return [lastQuarter.startOf('quarter').toDate(), lastQuarter.endOf('quarter').toDate()]
+    }
+  },
+  {
+    text: '今年',
+    value: () => {
+      return [dayjs().startOf('year').toDate(), dayjs().endOf('year').toDate()]
+    }
+  },
+  {
+    text: '去年',
+    value: () => {
+      const lastYear = dayjs().subtract(1, 'year')
+      return [lastYear.startOf('year').toDate(), lastYear.endOf('year').toDate()]
+    }
+  },
+  {
+    text: '最近7天',
+    value: () => {
+      return [dayjs().subtract(6, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近30天',
+    value: () => {
+      return [dayjs().subtract(29, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近90天',
+    value: () => {
+      return [dayjs().subtract(89, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近一年',
+    value: () => {
+      return [dayjs().subtract(1, 'year').toDate(), dayjs().toDate()]
+    }
+  }
+]
 
 /** 瑞都日报 汇总统计 */
 defineOptions({ name: 'IotRdDailyReportStatistics' })
@@ -209,7 +360,7 @@ const queryParams = reactive({
   status: undefined,
   processInstanceId: undefined,
   auditStatus: undefined,
-  createTime: [],
+  createTime: []
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
@@ -228,8 +379,8 @@ const tableContainerRef = ref()
 // 计算属性:获取所有动态列(去重的unit)
 const dynamicColumns = computed(() => {
   const units = new Set()
-  list.value.forEach(item => {
-    item.items.forEach(subItem => {
+  list.value.forEach((item) => {
+    item.items.forEach((subItem) => {
       if (subItem.unit) {
         units.add(subItem.unit)
       }
@@ -241,7 +392,7 @@ const dynamicColumns = computed(() => {
 // 根据unit获取对应workload
 const getWorkloadByUnit = (items, unit) => {
   if (!items || !Array.isArray(items)) return ''
-  const targetItem = items.find(item => item.unit === unit)
+  const targetItem = items.find((item) => item.unit === unit)
   return targetItem ? targetItem.workload : ''
 }
 
@@ -261,85 +412,85 @@ const columnWidths = ref({
 
 // 计算文本宽度
 const getTextWidth = (text: string, fontSize = 14) => {
-  const span = document.createElement('span');
-  span.style.visibility = 'hidden';
-  span.style.position = 'absolute';
-  span.style.whiteSpace = 'nowrap';
-  span.style.fontSize = `${fontSize}px`;
-  span.style.fontFamily = 'inherit';
-  span.innerText = text;
-
-  document.body.appendChild(span);
-  const width = span.offsetWidth;
-  document.body.removeChild(span);
-
-  return width;
-};
+  const span = document.createElement('span')
+  span.style.visibility = 'hidden'
+  span.style.position = 'absolute'
+  span.style.whiteSpace = 'nowrap'
+  span.style.fontSize = `${fontSize}px`
+  span.style.fontFamily = 'inherit'
+  span.innerText = text
+
+  document.body.appendChild(span)
+  const width = span.offsetWidth
+  document.body.removeChild(span)
+
+  return width
+}
 
 // 计算列宽度
 const calculateColumnWidths = () => {
-  const MIN_WIDTH = 80; // 最小列宽
-  const PADDING = 25; // 列内边距
+  const MIN_WIDTH = 80 // 最小列宽
+  const PADDING = 25 // 列内边距
 
   // 确保表格容器存在
-  if (!tableContainerRef.value?.$el) return;
+  if (!tableContainerRef.value?.$el) return
 
-  const newWidths: Record<string, string> = {};
+  const newWidths: Record<string, string> = {}
 
   // 计算各列宽度的函数
   const calculateColumnWidth = (key: string, label: string, getValue: Function) => {
-    const headerWidth = getTextWidth(label) + PADDING;
-    let contentMaxWidth = MIN_WIDTH;
+    const headerWidth = getTextWidth(label) + PADDING
+    let contentMaxWidth = MIN_WIDTH
 
     // 计算内容最大宽度
     list.value.forEach((row, index) => {
-      let text = '';
+      let text = ''
       if (key === 'rdStatus') {
         // 特殊处理字典列,这里简化处理,实际应该获取字典标签
-        text = String(row[key] || '');
+        text = String(row[key] || '')
       } else if (key.includes('Date') || key === 'createTime') {
         // 日期列使用格式化后的值
-        text = dateFormatter(null, null, row[key]) || '';
+        text = dateFormatter(null, null, row[key]) || ''
       } else {
-        text = String(getValue ? getValue(row, index) : (row[key] || ''));
+        text = String(getValue ? getValue(row, index) : row[key] || '')
       }
 
-      const textWidth = getTextWidth(text) + PADDING;
-      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth;
-    });
+      const textWidth = getTextWidth(text) + PADDING
+      if (textWidth > contentMaxWidth) contentMaxWidth = textWidth
+    })
 
     // 取标题宽度和内容最大宽度的较大值
-    const finalWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH);
-    newWidths[key] = `${finalWidth}px`;
-  };
+    const finalWidth = Math.max(headerWidth, contentMaxWidth, MIN_WIDTH)
+    newWidths[key] = `${finalWidth}px`
+  }
 
   // 计算施工状态列宽度(使用字典标签文本计算)
   calculateColumnWidth('rdStatus', '施工状态', (row: any) => {
     // 用字典标签(如"完工")而非原始编码(如"wg")计算宽度
-    return getDictLabel(DICT_TYPE.PMS_PROJECT_RD_STATUS, row.rdStatus) || row.rdStatus;
-  });
+    return getDictLabel(DICT_TYPE.PMS_PROJECT_RD_STATUS, row.rdStatus) || row.rdStatus
+  })
 
   // 计算各列宽度
-  calculateColumnWidth('projectDeptName', '项目部', (row: any) => row.projectDeptName);
-  calculateColumnWidth('deptName', '队伍', (row: any) => row.deptName);
-  calculateColumnWidth('manufactureName', '甲方', (row: any) => row.manufactureName);
-  calculateColumnWidth('wellName', '井号', (row: any) => row.wellName);
-  calculateColumnWidth('techniques', '工艺', (row: any) => row.techniques);
-  calculateColumnWidth('workloadDesign', '总工作量', (row: any) => row.workloadDesign);
+  calculateColumnWidth('projectDeptName', '项目部', (row: any) => row.projectDeptName)
+  calculateColumnWidth('deptName', '队伍', (row: any) => row.deptName)
+  calculateColumnWidth('manufactureName', '甲方', (row: any) => row.manufactureName)
+  calculateColumnWidth('wellName', '井号', (row: any) => row.wellName)
+  calculateColumnWidth('techniques', '工艺', (row: any) => row.techniques)
+  calculateColumnWidth('workloadDesign', '总工作量', (row: any) => row.workloadDesign)
 
   // 操作列固定宽度
-  newWidths.operation = '120px';
+  newWidths.operation = '120px'
   // id列固定宽度(虽然隐藏)
-  newWidths.id = '80px';
+  newWidths.id = '80px'
 
   // 更新列宽配置
-  columnWidths.value = newWidths;
+  columnWidths.value = newWidths
 
   // 触发表格重新布局
   nextTick(() => {
-    tableRef.value?.doLayout();
-  });
-};
+    tableRef.value?.doLayout()
+  })
+}
 
 /** 查询列表 */
 const getList = async () => {
@@ -354,8 +505,8 @@ const getList = async () => {
     handleFrontendPagination()
     // 获取数据后计算列宽
     nextTick(() => {
-      calculateColumnWidths();
-    });
+      calculateColumnWidths()
+    })
   } finally {
     loading.value = false
   }
@@ -429,7 +580,7 @@ const handleApprove = async (id: number) => {
       name: 'FillDailyReportForm',
       params: {
         id: id.toString(),
-        mode: 'approval'  // 添加审批模式标识
+        mode: 'approval' // 添加审批模式标识
       }
     })
   } catch (error) {
@@ -466,7 +617,7 @@ const handleExport = async () => {
 }
 
 // 声明 ResizeObserver 实例
-let resizeObserver: ResizeObserver | null = null;
+let resizeObserver: ResizeObserver | null = null
 
 /** 初始化 **/
 onMounted(() => {
@@ -475,55 +626,56 @@ onMounted(() => {
   if (tableContainerRef.value?.$el) {
     resizeObserver = new ResizeObserver(() => {
       // 使用防抖避免频繁触发
-      clearTimeout((window as any).resizeTimer);
-      (window as any).resizeTimer = setTimeout(() => {
-        calculateColumnWidths();
-      }, 100);
-    });
-    resizeObserver.observe(tableContainerRef.value.$el);
+      clearTimeout((window as any).resizeTimer)
+      ;(window as any).resizeTimer = setTimeout(() => {
+        calculateColumnWidths()
+      }, 100)
+    })
+    resizeObserver.observe(tableContainerRef.value.$el)
   }
 })
 
 onUnmounted(() => {
   // 清除 ResizeObserver
   if (resizeObserver && tableContainerRef.value?.$el) {
-    resizeObserver.unobserve(tableContainerRef.value.$el);
-    resizeObserver = null;
+    resizeObserver.unobserve(tableContainerRef.value.$el)
+    resizeObserver = null
   }
 
   // 清除定时器
   if ((window as any).resizeTimer) {
-    clearTimeout((window as any).resizeTimer);
+    clearTimeout((window as any).resizeTimer)
   }
 })
 
 // 监听查询参数变化,实现前端分页
+watch([() => queryParams.pageNo, () => queryParams.pageSize], () => {
+  handleFrontendPagination()
+})
+
+// 监听列表数据变化重新计算列宽
 watch(
-  [() => queryParams.pageNo, () => queryParams.pageSize],
+  list,
   () => {
-    handleFrontendPagination()
-  }
+    nextTick(calculateColumnWidths)
+  },
+  { deep: true }
 )
-
-// 监听列表数据变化重新计算列宽
-watch(list, () => {
-  nextTick(calculateColumnWidths)
-}, { deep: true })
 </script>
 
 <style scoped>
 /* 确保表格单元格内容不换行 */
 :deep(.el-table .cell) {
-white-space: nowrap;
+  white-space: nowrap;
 }
 
 /* 确保表格列标题不换行 */
 :deep(.el-table th > .cell) {
-white-space: nowrap;
+  white-space: nowrap;
 }
 
 /* 调整表格最小宽度,确保内容完全显示 */
 :deep(.el-table) {
-min-width: 100%;
+  min-width: 100%;
 }
 </style>

+ 42 - 34
src/views/pms/iotrhdailyreport/DeptTree2.vue

@@ -1,6 +1,12 @@
 <template>
-  <div class="head-container" style="display: flex;flex-direction: row;">
-    <el-input v-model="deptName" class="mb-18px" clearable placeholder="请输入部门名称">
+  <div class="head-container" style="display: flex; flex-direction: row">
+    <el-input
+      size="default"
+      v-model="deptName"
+      class="mb-18px"
+      clearable
+      placeholder="请输入部门名称"
+    >
       <template #prefix>
         <Icon icon="ep:search" />
       </template>
@@ -21,11 +27,7 @@
       style="height: 52em"
     />
   </div>
-  <div
-    v-show="menuVisible"
-    class="custom-menu"
-    :style="{ left: menuX + 'px', top: menuY + 'px' }"
-  >
+  <div v-show="menuVisible" class="custom-menu" :style="{ left: menuX + 'px', top: menuY + 'px' }">
     <ul>
       <li @click="handleMenuClick('add')">新增子节点</li>
       <li @click="handleMenuClick('edit')">重命名</li>
@@ -39,18 +41,17 @@ import { ElTree } from 'element-plus'
 import * as DeptApi from '@/api/system/dept'
 import { defaultProps, handleTree } from '@/utils/tree'
 import { useTreeStore } from '@/store/modules/usersTreeStore'
-import {specifiedSimpleDepts} from "@/api/system/dept";
 defineOptions({ name: 'DeptTree2' })
 
 const deptName = ref('')
 const deptList = ref<Tree[]>([]) // 树形结构
 const treeRef = ref<InstanceType<typeof ElTree>>()
-const menuVisible = ref(false);
-const menuX = ref(0);
-const menuY = ref(0);
+const menuVisible = ref(false)
+const menuX = ref(0)
+const menuY = ref(0)
 const firstLevelKeys = ref([])
-let selectedNode = null;
-const treeStore = useTreeStore();
+let selectedNode = null
+const treeStore = useTreeStore()
 
 // props 定义,接收 deptId 参数
 interface Props {
@@ -62,12 +63,12 @@ const props = withDefaults(defineProps<Props>(), {
 })
 
 const handleRightClick = (event, { node, data }) => {
-  event.preventDefault();
-  menuX.value = event.clientX;
-  menuY.value = event.clientY;
-  selectedNode = data; // 存储当前操作的节点数据 ‌:ml-citation{ref="7" data="citationList"}
+  event.preventDefault()
+  menuX.value = event.clientX
+  menuY.value = event.clientY
+  selectedNode = data // 存储当前操作的节点数据 ‌:ml-citation{ref="7" data="citationList"}
   //menuVisible.value = true;
-};
+}
 const treeContainer = ref(null)
 const setHeight = () => {
   if (!treeContainer.value) return
@@ -76,25 +77,25 @@ const setHeight = () => {
   treeContainer.value.style.height = `${windowHeight * 0.78}px` // 60px 底部预留
 }
 const handleMenuClick = (action) => {
-  switch(action) {
+  switch (action) {
     case 'add':
       // 调用新增节点逻辑 ‌:ml-citation{ref="4" data="citationList"}
-      break;
+      break
     case 'edit':
       // 调用编辑节点逻辑 ‌:ml-citation{ref="7" data="citationList"}
-      break;
+      break
     case 'delete':
       // 调用删除节点逻辑 ‌:ml-citation{ref="4" data="citationList"}
-      break;
+      break
   }
-  menuVisible.value = false;
-};
+  menuVisible.value = false
+}
 /** 获得部门树 */
 const getTree = async () => {
   const res = await DeptApi.specifiedSimpleDepts(props.deptId)
   deptList.value = []
   deptList.value.push(...handleTree(res))
-  firstLevelKeys.value = deptList.value.map(node => node.id);
+  firstLevelKeys.value = deptList.value.map((node) => node.id)
 }
 
 /** 基于名字过滤 */
@@ -106,7 +107,7 @@ const filterNode = (name: string, data: Tree) => {
 /** 处理部门被点击 */
 const handleNodeClick = async (row: { [key: string]: any }) => {
   emits('node-click', row)
-  treeStore.setSelectedId(row.id);
+  treeStore.setSelectedId(row.id)
 }
 const emits = defineEmits(['node-click'])
 
@@ -116,11 +117,14 @@ watch(deptName, (val) => {
 })
 
 // 监听 deptId 变化,当父组件改变 deptId 时重新加载树
-watch(() => props.deptId, (newVal, oldVal) => {
-  if (newVal !== oldVal) {
-    getTree()
+watch(
+  () => props.deptId,
+  (newVal, oldVal) => {
+    if (newVal !== oldVal) {
+      getTree()
+    }
   }
-})
+)
 
 /** 初始化 */
 onMounted(async () => {
@@ -135,26 +139,30 @@ onUnmounted(() => {
 <style lang="scss" scoped>
 .custom-menu {
   position: fixed;
+  z-index: 1000;
   background: white;
   border: 1px solid #ccc;
-  box-shadow: 2px 2px 5px rgba(0,0,0,0.1);
-  z-index: 1000;
+  box-shadow: 2px 2px 5px rgb(0 0 0 / 10%);
 }
+
 .custom-menu ul {
-  list-style: none;
   padding: 0;
   margin: 0;
+  list-style: none;
 }
+
 .custom-menu li {
   padding: 8px 20px;
   cursor: pointer;
 }
+
 .custom-menu li:hover {
   background: #f5f5f5;
 }
+
 .tree-container {
-  overflow-y: auto;
   min-width: 100%;
+  overflow-y: auto;
   border: 1px solid #e4e7ed;
   border-radius: 4px;
 }

Разлика између датотеке није приказан због своје велике величине
+ 512 - 280
src/views/pms/iotrhdailyreport/index.vue


+ 416 - 0
src/views/pms/iotrhdailyreport/summary.vue

@@ -0,0 +1,416 @@
+<script setup lang="ts">
+import DeptTree2 from '@/views/pms/iotrhdailyreport/DeptTree2.vue'
+import quarterOfYear from 'dayjs/plugin/quarterOfYear'
+import dayjs from 'dayjs'
+import { IotRhDailyReportApi } from '@/api/pms/iotrhdailyreport'
+import { useDebounceFn } from '@vueuse/core'
+import CountTo from '@/components/count-to1.vue'
+
+dayjs.extend(quarterOfYear)
+
+const rangeShortcuts = [
+  {
+    text: '今天',
+    value: () => {
+      const today = dayjs()
+      return [today.startOf('day').toDate(), today.endOf('day').toDate()]
+    }
+  },
+  {
+    text: '昨天',
+    value: () => {
+      const yesterday = dayjs().subtract(1, 'day')
+      return [yesterday.startOf('day').toDate(), yesterday.endOf('day').toDate()]
+    }
+  },
+  {
+    text: '本周',
+    value: () => {
+      return [dayjs().startOf('week').toDate(), dayjs().endOf('week').toDate()]
+    }
+  },
+  {
+    text: '上周',
+    value: () => {
+      const lastWeek = dayjs().subtract(1, 'week')
+      return [lastWeek.startOf('week').toDate(), lastWeek.endOf('week').toDate()]
+    }
+  },
+  {
+    text: '本月',
+    value: () => {
+      return [dayjs().startOf('month').toDate(), dayjs().endOf('month').toDate()]
+    }
+  },
+  {
+    text: '上月',
+    value: () => {
+      const lastMonth = dayjs().subtract(1, 'month')
+      return [lastMonth.startOf('month').toDate(), lastMonth.endOf('month').toDate()]
+    }
+  },
+  {
+    text: '本季度',
+    value: () => {
+      return [dayjs().startOf('quarter').toDate(), dayjs().endOf('quarter').toDate()]
+    }
+  },
+  {
+    text: '上季度',
+    value: () => {
+      const lastQuarter = dayjs().subtract(1, 'quarter')
+      return [lastQuarter.startOf('quarter').toDate(), lastQuarter.endOf('quarter').toDate()]
+    }
+  },
+  {
+    text: '今年',
+    value: () => {
+      return [dayjs().startOf('year').toDate(), dayjs().endOf('year').toDate()]
+    }
+  },
+  {
+    text: '去年',
+    value: () => {
+      const lastYear = dayjs().subtract(1, 'year')
+      return [lastYear.startOf('year').toDate(), lastYear.endOf('year').toDate()]
+    }
+  },
+  {
+    text: '最近7天',
+    value: () => {
+      return [dayjs().subtract(6, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近30天',
+    value: () => {
+      return [dayjs().subtract(29, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近90天',
+    value: () => {
+      return [dayjs().subtract(89, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近一年',
+    value: () => {
+      return [dayjs().subtract(1, 'year').toDate(), dayjs().toDate()]
+    }
+  }
+]
+
+interface Query {
+  pageNo: number
+  pageSize: number
+  deptId: number
+  contractName?: string
+  taskName?: string
+  createTime: string[]
+}
+
+const id = 157
+
+const query = ref<Query>({
+  pageNo: 1,
+  pageSize: 10,
+  deptId: 157,
+  createTime: []
+})
+
+const totalWorkKeys = [
+  ['totalCount', '个', '总数', 'i-tabler:report-analytics text-sky'],
+  [
+    'alreadyReported',
+    '个',
+    '已填报',
+    'i-material-symbols:check-circle-outline-rounded text-emerald'
+  ],
+  ['notReported', '个', '未填报', 'i-material-symbols:cancel-outline-rounded text-rose'],
+  [
+    'totalFuelConsumption',
+    '吨',
+    '累计油耗',
+    'i-material-symbols:directions-car-outline-rounded text-sky'
+  ],
+  [
+    'totalPowerConsumption',
+    'KWH',
+    '累计用电量',
+    'i-material-symbols:electric-bolt-outline-rounded text-sky'
+  ],
+  [
+    'totalWaterInjection',
+    '方',
+    '累计注水量',
+    'i-material-symbols:water-drop-outline-rounded text-sky'
+  ],
+  ['totalGasInjection', '万方', '累计注气量', 'i-material-symbols:cloud-outline text-sky']
+]
+
+const totalWork = ref({
+  totalCount: 0,
+  alreadyReported: 0,
+  notReported: 0,
+  totalFuelConsumption: 0,
+  totalPowerConsumption: 0,
+  totalWaterInjection: 0,
+  totalGasInjection: 0
+})
+
+const totalLoading = ref(false)
+
+const getTotal = useDebounceFn(async () => {
+  totalLoading.value = true
+
+  const { pageNo, pageSize, ...other } = query.value
+
+  try {
+    let res1: any[]
+    if (query.value.createTime.length !== 0) {
+      res1 = await IotRhDailyReportApi.rhDailyReportStatistics({
+        createTime: query.value.createTime
+      })
+
+      totalWork.value.totalCount = res1[0].count
+      totalWork.value.alreadyReported = res1[1].count
+      totalWork.value.notReported = res1[2].count
+    }
+
+    const res2 = await IotRhDailyReportApi.totalWorkload(other)
+
+    totalWork.value = {
+      ...totalWork.value,
+      ...res2,
+      totalGasInjection: (res2.totalGasInjection || 0) / 10000
+    }
+  } finally {
+    totalLoading.value = false
+  }
+}, 1000)
+
+interface List {
+  id: number | null
+  name: string | null
+  type: '1' | '2' | '3'
+  cumulativeGasInjection: number | null
+  cumulativeWaterInjection: number | null
+  cumulativePowerConsumption: number | null
+  cumulativeFuelConsumption: number | null
+  transitTime: number | null
+}
+
+const total = ref<number>(1000)
+
+const list = ref<List[]>([])
+
+const type = ref('2')
+
+const columns = (type: string) => {
+  return [
+    {
+      label: type === '2' ? '项目部' : '队伍',
+      prop: 'name'
+    },
+    {
+      label: '累计注气量(万方)',
+      prop: 'cumulativeGasInjection'
+    },
+    {
+      label: '累计注水量(方)',
+      prop: 'cumulativeWaterInjection'
+    },
+    {
+      label: '累计用电量(KWH)',
+      prop: 'cumulativePowerConsumption'
+    },
+    {
+      label: '累计油耗(吨)',
+      prop: 'cumulativeFuelConsumption'
+    }
+  ]
+}
+
+const listLoading = ref(false)
+
+const formatter = (row: List, column: any) => {
+  return row[column.property] ?? 0
+}
+
+const getList = useDebounceFn(async () => {
+  listLoading.value = true
+  try {
+    const res = (await IotRhDailyReportApi.getIotRhDailyReportSummary(query.value)) as {
+      total: number
+      list: any[]
+    }
+
+    const { total: resTotal, list: resList } = res
+
+    total.value = resTotal
+
+    type.value = resList[0]?.type || '2'
+
+    list.value = resList.map(
+      ({ id, projectDeptIa, projectDeptName, teamId, teamName, sort, taskId, type, ...other }) => ({
+        id: type === '2' ? projectDeptIa : teamId,
+        name: type === '2' ? projectDeptName : teamName,
+        ...other,
+        cumulativeGasInjection: (other.cumulativeGasInjection || 0) / 10000
+      })
+    )
+  } finally {
+    listLoading.value = false
+  }
+}, 1000)
+
+const handleDeptNodeClick = (node: any) => {
+  query.value.deptId = node.id
+  handleQuery()
+}
+
+const handleQuery = (setPage = true) => {
+  if (setPage) {
+    query.value.pageNo = 1
+  }
+  getTotal()
+  getList()
+}
+
+const resetQuery = () => {
+  query.value = {
+    pageNo: 1,
+    pageSize: 10,
+    deptId: 157,
+    contractName: '',
+    taskName: '',
+    createTime: []
+  }
+  handleQuery()
+}
+
+watch(
+  () => query.value.createTime,
+  () => handleQuery(false)
+)
+
+onMounted(() => {
+  handleQuery()
+})
+</script>
+
+<template>
+  <div class="grid grid-cols-[16%_1fr] gap-5">
+    <div class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow p-4">
+      <DeptTree2 :deptId="id" @node-click="handleDeptNodeClick" />
+    </div>
+    <div class="grid grid-rows-[62px_164px_1fr] h-full gap-5">
+      <el-form
+        size="default"
+        class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"
+      >
+        <div class="flex items-center gap-8">
+          <el-form-item label="项目">
+            <el-input
+              v-model="query.contractName"
+              placeholder="请输入项目"
+              clearable
+              @keyup.enter="handleQuery()"
+              class="!w-240px"
+            />
+          </el-form-item>
+          <el-form-item label="任务">
+            <el-input
+              v-model="query.taskName"
+              placeholder="请输入任务"
+              clearable
+              @keyup.enter="handleQuery()"
+              class="!w-240px"
+            />
+          </el-form-item>
+          <el-form-item label="创建时间">
+            <el-date-picker
+              v-model="query.createTime"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              type="daterange"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              :shortcuts="rangeShortcuts"
+              class="!w-220px"
+            />
+          </el-form-item>
+        </div>
+        <el-form-item>
+          <el-button type="primary" @click="handleQuery()">
+            <Icon icon="ep:search" class="mr-5px" /> 搜索
+          </el-button>
+          <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        </el-form-item>
+      </el-form>
+      <div class="grid grid-cols-7 gap-8">
+        <div
+          v-for="info in totalWorkKeys"
+          :key="info[0]"
+          class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow p-4 flex flex-col items-center justify-center gap-2"
+        >
+          <div class="size-7.5" :class="info[3]"></div>
+          <count-to class="text-2xl font-medium" :start-val="0" :end-val="totalWork[info[0]]">
+            <span>—</span>
+          </count-to>
+          <div class="text-xs font-medium text-[var(--el-text-color-regular)]">{{ info[1] }}</div>
+          <div class="text-sm font-medium text-[var(--el-text-color-regular)]">{{ info[2] }}</div>
+        </div>
+      </div>
+      <div class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow pt-4 px-8">
+        <el-table
+          v-loading="listLoading"
+          :data="list"
+          :stripe="true"
+          :style="{ width: '100%' }"
+          max-height="600"
+          class="min-h-143"
+          show-overflow-tooltip
+        >
+          <el-table-column
+            v-for="item in columns(type)"
+            :key="item.prop"
+            :label="item.label"
+            :prop="item.prop"
+            align="center"
+            :formatter="formatter"
+          />
+        </el-table>
+
+        <Pagination
+          class="mt-8"
+          :total="total"
+          v-model:page="query.pageNo"
+          v-model:limit="query.pageSize"
+          @pagination="getList"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+:deep(.el-form-item) {
+  margin-bottom: 0;
+}
+
+:deep(.el-table) {
+  border-top-right-radius: 8px;
+  border-top-left-radius: 8px;
+
+  .el-table__cell {
+    height: 52px;
+  }
+
+  .el-table__header-wrapper {
+    .el-table__cell {
+      background: var(--el-fill-color-light);
+    }
+  }
+}
+</style>

Разлика између датотеке није приказан због своје велике величине
+ 654 - 274
src/views/pms/iotrydailyreport/index.vue


+ 406 - 0
src/views/pms/iotrydailyreport/summary.vue

@@ -0,0 +1,406 @@
+<script setup lang="ts">
+import DeptTree2 from '@/views/pms/iotrhdailyreport/DeptTree2.vue'
+import quarterOfYear from 'dayjs/plugin/quarterOfYear'
+import dayjs from 'dayjs'
+import { IotRyDailyReportApi } from '@/api/pms/iotrydailyreport'
+import { useDebounceFn } from '@vueuse/core'
+import CountTo from '@/components/count-to1.vue'
+
+dayjs.extend(quarterOfYear)
+
+const rangeShortcuts = [
+  {
+    text: '今天',
+    value: () => {
+      const today = dayjs()
+      return [today.startOf('day').toDate(), today.endOf('day').toDate()]
+    }
+  },
+  {
+    text: '昨天',
+    value: () => {
+      const yesterday = dayjs().subtract(1, 'day')
+      return [yesterday.startOf('day').toDate(), yesterday.endOf('day').toDate()]
+    }
+  },
+  {
+    text: '本周',
+    value: () => {
+      return [dayjs().startOf('week').toDate(), dayjs().endOf('week').toDate()]
+    }
+  },
+  {
+    text: '上周',
+    value: () => {
+      const lastWeek = dayjs().subtract(1, 'week')
+      return [lastWeek.startOf('week').toDate(), lastWeek.endOf('week').toDate()]
+    }
+  },
+  {
+    text: '本月',
+    value: () => {
+      return [dayjs().startOf('month').toDate(), dayjs().endOf('month').toDate()]
+    }
+  },
+  {
+    text: '上月',
+    value: () => {
+      const lastMonth = dayjs().subtract(1, 'month')
+      return [lastMonth.startOf('month').toDate(), lastMonth.endOf('month').toDate()]
+    }
+  },
+  {
+    text: '本季度',
+    value: () => {
+      return [dayjs().startOf('quarter').toDate(), dayjs().endOf('quarter').toDate()]
+    }
+  },
+  {
+    text: '上季度',
+    value: () => {
+      const lastQuarter = dayjs().subtract(1, 'quarter')
+      return [lastQuarter.startOf('quarter').toDate(), lastQuarter.endOf('quarter').toDate()]
+    }
+  },
+  {
+    text: '今年',
+    value: () => {
+      return [dayjs().startOf('year').toDate(), dayjs().endOf('year').toDate()]
+    }
+  },
+  {
+    text: '去年',
+    value: () => {
+      const lastYear = dayjs().subtract(1, 'year')
+      return [lastYear.startOf('year').toDate(), lastYear.endOf('year').toDate()]
+    }
+  },
+  {
+    text: '最近7天',
+    value: () => {
+      return [dayjs().subtract(6, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近30天',
+    value: () => {
+      return [dayjs().subtract(29, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近90天',
+    value: () => {
+      return [dayjs().subtract(89, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近一年',
+    value: () => {
+      return [dayjs().subtract(1, 'year').toDate(), dayjs().toDate()]
+    }
+  }
+]
+
+interface Query {
+  pageNo: number
+  pageSize: number
+  deptId: number
+  contractName?: string
+  taskName?: string
+  createTime: string[]
+  projectClassification: 1 | 2
+}
+
+const id = 158
+
+const query = ref<Query>({
+  pageNo: 1,
+  pageSize: 10,
+  deptId: 158,
+  createTime: [],
+  projectClassification: 1
+})
+
+const totalWorkKeys = [
+  ['totalCount', '个', '总数', 'i-tabler:report-analytics text-sky'],
+  [
+    'alreadyReported',
+    '个',
+    '已填报',
+    'i-material-symbols:check-circle-outline-rounded text-emerald'
+  ],
+  ['notReported', '个', '未填报', 'i-material-symbols:cancel-outline-rounded text-rose'],
+  [
+    'totalFuelConsumption',
+    '吨',
+    '累计油耗',
+    'i-material-symbols:directions-car-outline-rounded text-sky'
+  ],
+  [
+    'totalPowerConsumption',
+    'KWH',
+    '累计用电量',
+    'i-material-symbols:electric-bolt-outline-rounded text-sky'
+  ],
+  ['totalFootage', 'M', '累计进尺', 'i-solar:ruler-bold text-sky']
+]
+
+const totalWork = ref({
+  totalCount: 0,
+  alreadyReported: 0,
+  notReported: 0,
+  totalFuelConsumption: 0,
+  totalPowerConsumption: 0,
+  totalFootage: 0
+})
+
+const totalLoading = ref(false)
+
+const getTotal = useDebounceFn(async () => {
+  totalLoading.value = true
+
+  const { pageNo, pageSize, ...other } = query.value
+
+  try {
+    let res1: any[]
+    if (query.value.createTime.length !== 0) {
+      res1 = await IotRyDailyReportApi.ryDailyReportStatistics({
+        createTime: query.value.createTime,
+        projectClassification: query.value.projectClassification
+      })
+
+      totalWork.value.totalCount = res1[0].count
+      totalWork.value.alreadyReported = res1[1].count
+      totalWork.value.notReported = res1[2].count
+    }
+
+    const res2 = await IotRyDailyReportApi.totalWorkload(other)
+
+    totalWork.value = {
+      ...totalWork.value,
+      ...res2,
+      totalGasInjection: (res2.totalGasInjection || 0) / 10000
+    }
+  } finally {
+    totalLoading.value = false
+  }
+}, 1000)
+
+interface List {
+  id: number | null
+  name: string | null
+  type: '1' | '2' | '3'
+  cumulativeGasInjection: number | null
+  cumulativeWaterInjection: number | null
+  cumulativePowerConsumption: number | null
+  cumulativeFuelConsumption: number | null
+  transitTime: number | null
+}
+
+const total = ref<number>(1000)
+
+const list = ref<List[]>([])
+
+const type = ref('2')
+
+const columns = (type: string) => {
+  return [
+    {
+      label: type === '2' ? '项目部' : '队伍',
+      prop: 'name'
+    },
+    {
+      label: '累计进尺(M)',
+      prop: 'cumulativeFootage'
+    },
+    {
+      label: '累计用电量(KWH)',
+      prop: 'cumulativePowerConsumption'
+    },
+    {
+      label: '累计油耗(吨)',
+      prop: 'cumulativeFuelConsumption'
+    }
+  ]
+}
+
+const listLoading = ref(false)
+
+const formatter = (row: List, column: any) => {
+  return row[column.property] ?? 0
+}
+
+const getList = useDebounceFn(async () => {
+  listLoading.value = true
+  try {
+    const res = (await IotRyDailyReportApi.getIotRyDailyReportSummary(query.value)) as {
+      total: number
+      list: any[]
+    }
+
+    const { total: resTotal, list: resList } = res
+
+    total.value = resTotal
+
+    type.value = resList[0]?.type || '2'
+
+    list.value = resList.map(
+      ({ id, projectDeptIa, projectDeptName, teamId, teamName, sort, taskId, type, ...other }) => ({
+        id: type === '2' ? projectDeptIa : teamId,
+        name: type === '2' ? projectDeptName : teamName,
+        ...other
+      })
+    )
+  } finally {
+    listLoading.value = false
+  }
+}, 1000)
+
+const handleDeptNodeClick = (node: any) => {
+  query.value.deptId = node.id
+  handleQuery()
+}
+
+const handleQuery = (setPage = true) => {
+  if (setPage) {
+    query.value.pageNo = 1
+  }
+  getTotal()
+  getList()
+}
+
+const resetQuery = () => {
+  query.value = {
+    pageNo: 1,
+    pageSize: 10,
+    deptId: 157,
+    createTime: [],
+    projectClassification: 1
+  }
+  handleQuery()
+}
+
+watch(
+  () => query.value.createTime,
+  () => handleQuery(false)
+)
+
+onMounted(() => {
+  handleQuery()
+})
+</script>
+
+<template>
+  <div class="grid grid-cols-[16%_1fr] gap-5">
+    <div class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow p-4">
+      <DeptTree2 :deptId="id" @node-click="handleDeptNodeClick" />
+    </div>
+    <div class="grid grid-rows-[62px_164px_1fr] h-full gap-5">
+      <el-form
+        size="default"
+        class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"
+      >
+        <div class="flex items-center gap-8">
+          <el-form-item label="项目">
+            <el-input
+              v-model="query.contractName"
+              placeholder="请输入项目"
+              clearable
+              @keyup.enter="handleQuery()"
+              class="!w-240px"
+            />
+          </el-form-item>
+          <el-form-item label="任务">
+            <el-input
+              v-model="query.taskName"
+              placeholder="请输入任务"
+              clearable
+              @keyup.enter="handleQuery()"
+              class="!w-240px"
+            />
+          </el-form-item>
+          <el-form-item label="创建时间">
+            <el-date-picker
+              v-model="query.createTime"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              type="daterange"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              :shortcuts="rangeShortcuts"
+              class="!w-220px"
+            />
+          </el-form-item>
+        </div>
+        <el-form-item>
+          <el-button type="primary" @click="handleQuery()">
+            <Icon icon="ep:search" class="mr-5px" /> 搜索
+          </el-button>
+          <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        </el-form-item>
+      </el-form>
+      <div class="grid grid-cols-6 gap-8">
+        <div
+          v-for="info in totalWorkKeys"
+          :key="info[0]"
+          class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow p-4 flex flex-col items-center justify-center gap-2"
+        >
+          <div class="size-7.5" :class="info[3]"></div>
+          <count-to class="text-2xl font-medium" :start-val="0" :end-val="totalWork[info[0]]">
+            <span>—</span>
+          </count-to>
+          <div class="text-xs font-medium text-[var(--el-text-color-regular)]">{{ info[1] }}</div>
+          <div class="text-sm font-medium text-[var(--el-text-color-regular)]">{{ info[2] }}</div>
+        </div>
+      </div>
+      <div class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow pt-4 px-8">
+        <el-table
+          v-loading="listLoading"
+          :data="list"
+          :stripe="true"
+          :style="{ width: '100%' }"
+          max-height="600"
+          class="min-h-143"
+          show-overflow-tooltip
+        >
+          <el-table-column
+            v-for="item in columns(type)"
+            :key="item.prop"
+            :label="item.label"
+            :prop="item.prop"
+            align="center"
+            :formatter="formatter"
+          />
+        </el-table>
+
+        <Pagination
+          class="mt-8"
+          :total="total"
+          v-model:page="query.pageNo"
+          v-model:limit="query.pageSize"
+          @pagination="getList"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+:deep(.el-form-item) {
+  margin-bottom: 0;
+}
+
+:deep(.el-table) {
+  border-top-right-radius: 8px;
+  border-top-left-radius: 8px;
+
+  .el-table__cell {
+    height: 52px;
+  }
+
+  .el-table__header-wrapper {
+    .el-table__cell {
+      background: var(--el-fill-color-light);
+    }
+  }
+}
+</style>

Разлика између датотеке није приказан због своје велике величине
+ 590 - 308
src/views/pms/iotrydailyreport/xjindex.vue


+ 411 - 0
src/views/pms/iotrydailyreport/xsummary.vue

@@ -0,0 +1,411 @@
+<script setup lang="ts">
+import DeptTree2 from '@/views/pms/iotrhdailyreport/DeptTree2.vue'
+import quarterOfYear from 'dayjs/plugin/quarterOfYear'
+import dayjs from 'dayjs'
+import { IotRyDailyReportApi } from '@/api/pms/iotrydailyreport'
+import { useDebounceFn } from '@vueuse/core'
+import CountTo from '@/components/count-to1.vue'
+
+dayjs.extend(quarterOfYear)
+
+const rangeShortcuts = [
+  {
+    text: '今天',
+    value: () => {
+      const today = dayjs()
+      return [today.startOf('day').toDate(), today.endOf('day').toDate()]
+    }
+  },
+  {
+    text: '昨天',
+    value: () => {
+      const yesterday = dayjs().subtract(1, 'day')
+      return [yesterday.startOf('day').toDate(), yesterday.endOf('day').toDate()]
+    }
+  },
+  {
+    text: '本周',
+    value: () => {
+      return [dayjs().startOf('week').toDate(), dayjs().endOf('week').toDate()]
+    }
+  },
+  {
+    text: '上周',
+    value: () => {
+      const lastWeek = dayjs().subtract(1, 'week')
+      return [lastWeek.startOf('week').toDate(), lastWeek.endOf('week').toDate()]
+    }
+  },
+  {
+    text: '本月',
+    value: () => {
+      return [dayjs().startOf('month').toDate(), dayjs().endOf('month').toDate()]
+    }
+  },
+  {
+    text: '上月',
+    value: () => {
+      const lastMonth = dayjs().subtract(1, 'month')
+      return [lastMonth.startOf('month').toDate(), lastMonth.endOf('month').toDate()]
+    }
+  },
+  {
+    text: '本季度',
+    value: () => {
+      return [dayjs().startOf('quarter').toDate(), dayjs().endOf('quarter').toDate()]
+    }
+  },
+  {
+    text: '上季度',
+    value: () => {
+      const lastQuarter = dayjs().subtract(1, 'quarter')
+      return [lastQuarter.startOf('quarter').toDate(), lastQuarter.endOf('quarter').toDate()]
+    }
+  },
+  {
+    text: '今年',
+    value: () => {
+      return [dayjs().startOf('year').toDate(), dayjs().endOf('year').toDate()]
+    }
+  },
+  {
+    text: '去年',
+    value: () => {
+      const lastYear = dayjs().subtract(1, 'year')
+      return [lastYear.startOf('year').toDate(), lastYear.endOf('year').toDate()]
+    }
+  },
+  {
+    text: '最近7天',
+    value: () => {
+      return [dayjs().subtract(6, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近30天',
+    value: () => {
+      return [dayjs().subtract(29, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近90天',
+    value: () => {
+      return [dayjs().subtract(89, 'day').toDate(), dayjs().toDate()]
+    }
+  },
+  {
+    text: '最近一年',
+    value: () => {
+      return [dayjs().subtract(1, 'year').toDate(), dayjs().toDate()]
+    }
+  }
+]
+
+interface Query {
+  pageNo: number
+  pageSize: number
+  deptId: number
+  contractName?: string
+  taskName?: string
+  createTime: string[]
+  projectClassification: 1 | 2
+}
+
+const id = 158
+
+const query = ref<Query>({
+  pageNo: 1,
+  pageSize: 10,
+  deptId: 158,
+  createTime: [],
+  projectClassification: 2
+})
+
+const totalWorkKeys = [
+  ['totalCount', '个', '总数', 'i-tabler:report-analytics text-sky'],
+  [
+    'alreadyReported',
+    '个',
+    '已填报',
+    'i-material-symbols:check-circle-outline-rounded text-emerald'
+  ],
+  ['notReported', '个', '未填报', 'i-material-symbols:cancel-outline-rounded text-rose'],
+  [
+    'totalFuelConsumption',
+    '吨',
+    '累计油耗',
+    'i-material-symbols:directions-car-outline-rounded text-sky'
+  ],
+  [
+    'totalPowerConsumption',
+    'KWH',
+    '累计用电量',
+    'i-material-symbols:electric-bolt-outline-rounded text-sky'
+  ],
+  ['constructionWells', '个', '累计施工井数', 'i-mdi:progress-wrench text-sky'],
+  ['completedWells', '个', '累计完工井数', 'i-mdi:wrench-check-outline text-emerald']
+]
+
+const totalWork = ref({
+  totalCount: 0,
+  alreadyReported: 0,
+  notReported: 0,
+  totalFuelConsumption: 0,
+  totalPowerConsumption: 0,
+  constructionWells: 0,
+  completedWells: 0
+})
+
+const totalLoading = ref(false)
+
+const getTotal = useDebounceFn(async () => {
+  totalLoading.value = true
+
+  const { pageNo, pageSize, ...other } = query.value
+
+  try {
+    let res1: any[]
+    if (query.value.createTime.length !== 0) {
+      res1 = await IotRyDailyReportApi.ryDailyReportStatistics({
+        createTime: query.value.createTime,
+        projectClassification: query.value.projectClassification
+      })
+
+      totalWork.value.totalCount = res1[0].count
+      totalWork.value.alreadyReported = res1[1].count
+      totalWork.value.notReported = res1[2].count
+    }
+
+    const res2 = await IotRyDailyReportApi.totalWorkload(other)
+
+    totalWork.value = {
+      ...totalWork.value,
+      ...res2
+    }
+  } finally {
+    totalLoading.value = false
+  }
+}, 1000)
+
+interface List {
+  id: number | null
+  name: string | null
+  type: '1' | '2' | '3'
+  cumulativeGasInjection: number | null
+  cumulativeWaterInjection: number | null
+  cumulativePowerConsumption: number | null
+  cumulativeFuelConsumption: number | null
+  transitTime: number | null
+}
+
+const total = ref<number>(1000)
+
+const list = ref<List[]>([])
+
+const type = ref('2')
+
+const columns = (type: string) => {
+  return [
+    {
+      label: type === '2' ? '项目部' : '队伍',
+      prop: 'name'
+    },
+    {
+      label: '累计施工井数',
+      prop: 'cumulativeConstructWells'
+    },
+    {
+      label: '累计完工井数',
+      prop: 'cumulativeCompletedWells'
+    },
+    {
+      label: '累计用电量(KWH)',
+      prop: 'cumulativePowerConsumption'
+    },
+    {
+      label: '累计油耗(吨)',
+      prop: 'cumulativeFuelConsumption'
+    }
+  ]
+}
+
+const listLoading = ref(false)
+
+const formatter = (row: List, column: any) => {
+  return row[column.property] ?? 0
+}
+
+const getList = useDebounceFn(async () => {
+  listLoading.value = true
+  try {
+    const res = (await IotRyDailyReportApi.getIotRyDailyReportSummary(query.value)) as {
+      total: number
+      list: any[]
+    }
+
+    const { total: resTotal, list: resList } = res
+
+    total.value = resTotal
+
+    type.value = resList[0]?.type || '2'
+
+    list.value = resList.map(
+      ({ id, projectDeptIa, projectDeptName, teamId, teamName, sort, taskId, type, ...other }) => ({
+        id: type === '2' ? projectDeptIa : teamId,
+        name: type === '2' ? projectDeptName : teamName,
+        ...other
+      })
+    )
+  } finally {
+    listLoading.value = false
+  }
+}, 1000)
+
+const handleDeptNodeClick = (node: any) => {
+  query.value.deptId = node.id
+  handleQuery()
+}
+
+const handleQuery = (setPage = true) => {
+  if (setPage) {
+    query.value.pageNo = 1
+  }
+  getTotal()
+  getList()
+}
+
+const resetQuery = () => {
+  query.value = {
+    pageNo: 1,
+    pageSize: 10,
+    deptId: 157,
+    createTime: [],
+    projectClassification: 1
+  }
+  handleQuery()
+}
+
+watch(
+  () => query.value.createTime,
+  () => handleQuery(false)
+)
+
+onMounted(() => {
+  handleQuery()
+})
+</script>
+
+<template>
+  <div class="grid grid-cols-[16%_1fr] gap-5">
+    <div class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow p-4">
+      <DeptTree2 :deptId="id" @node-click="handleDeptNodeClick" />
+    </div>
+    <div class="grid grid-rows-[62px_164px_1fr] h-full gap-5">
+      <el-form
+        size="default"
+        class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"
+      >
+        <div class="flex items-center gap-8">
+          <el-form-item label="项目">
+            <el-input
+              v-model="query.contractName"
+              placeholder="请输入项目"
+              clearable
+              @keyup.enter="handleQuery()"
+              class="!w-240px"
+            />
+          </el-form-item>
+          <el-form-item label="任务">
+            <el-input
+              v-model="query.taskName"
+              placeholder="请输入任务"
+              clearable
+              @keyup.enter="handleQuery()"
+              class="!w-240px"
+            />
+          </el-form-item>
+          <el-form-item label="创建时间">
+            <el-date-picker
+              v-model="query.createTime"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              type="daterange"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              :shortcuts="rangeShortcuts"
+              class="!w-220px"
+            />
+          </el-form-item>
+        </div>
+        <el-form-item>
+          <el-button type="primary" @click="handleQuery()">
+            <Icon icon="ep:search" class="mr-5px" /> 搜索
+          </el-button>
+          <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        </el-form-item>
+      </el-form>
+      <div class="grid grid-cols-7 gap-8">
+        <div
+          v-for="info in totalWorkKeys"
+          :key="info[0]"
+          class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow p-4 flex flex-col items-center justify-center gap-2"
+        >
+          <div class="size-7.5" :class="info[3]"></div>
+          <count-to class="text-2xl font-medium" :start-val="0" :end-val="totalWork[info[0]]">
+            <span>—</span>
+          </count-to>
+          <div class="text-xs font-medium text-[var(--el-text-color-regular)]">{{ info[1] }}</div>
+          <div class="text-sm font-medium text-[var(--el-text-color-regular)]">{{ info[2] }}</div>
+        </div>
+      </div>
+      <div class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow pt-4 px-8">
+        <el-table
+          v-loading="listLoading"
+          :data="list"
+          :stripe="true"
+          :style="{ width: '100%' }"
+          max-height="600"
+          class="min-h-143"
+          show-overflow-tooltip
+        >
+          <el-table-column
+            v-for="item in columns(type)"
+            :key="item.prop"
+            :label="item.label"
+            :prop="item.prop"
+            align="center"
+            :formatter="formatter"
+          />
+        </el-table>
+
+        <Pagination
+          class="mt-8"
+          :total="total"
+          v-model:page="query.pageNo"
+          v-model:limit="query.pageSize"
+          @pagination="getList"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+:deep(.el-form-item) {
+  margin-bottom: 0;
+}
+
+:deep(.el-table) {
+  border-top-right-radius: 8px;
+  border-top-left-radius: 8px;
+
+  .el-table__cell {
+    height: 52px;
+  }
+
+  .el-table__header-wrapper {
+    .el-table__cell {
+      background: var(--el-fill-color-light);
+    }
+  }
+}
+</style>

+ 5 - 1
uno.config.ts

@@ -2,7 +2,11 @@ import { defineConfig, toEscapedSelector as e, presetUno, presetIcons } from 'un
 // import transformerVariantGroup from '@unocss/transformer-variant-group'
 
 export default defineConfig({
-  content: { pipeline: { include: [/\.(vue|svelte|[jt]sx|mdx?|astro|elm|php|phtml|html)($|\?)/, 'src/**/*.{js,ts}'] } },
+  content: {
+    pipeline: {
+      include: [/\.(vue|svelte|[jt]sx|mdx?|astro|elm|php|phtml|html)($|\?)/, 'src/**/*.{js,ts}']
+    }
+  },
   // ...UnoCSS options
   rules: [
     [

Неке датотеке нису приказане због велике количине промена