Эх сурвалжийг харах

✨ feat(日报): 单纯页面

Zimo 1 өдөр өмнө
parent
commit
3506d33168

+ 518 - 0
src/views/report-statistics/daily-report.vue

@@ -0,0 +1,518 @@
+<script lang="ts" setup>
+import { IotRhDailyReportApi } from '@/api/pms/iotrhdailyreport'
+import { rangeShortcuts } from '@/utils/formatTime'
+import { useDebounceFn } from '@vueuse/core'
+import dayjs from 'dayjs'
+import { DICT_TYPE } from '@/utils/dict'
+import { TableColumnCtx } from 'element-plus/es/components/table/src/table-column/defaults'
+import { useUserStore } from '@/store/modules/user'
+
+defineOptions({ name: 'DailyReport' })
+
+const tab = ref('井')
+const tabOptions = ['井', '队伍']
+
+interface List {
+  createTime: number // 日期
+  deptName: string // 施工队伍
+  contractName: string // 项目
+  taskName: string // 任务
+  id: number
+  deptId: number
+  projectId: number
+  taskId: number
+  relocationDays: number // 搬迁安装天数
+  designInjection: string // 设计注气量(万方)
+  transitTime: number // 运行时效
+  dailyGasInjection: number // 注气量(万方)
+  dailyWaterInjection: number // 注水量(方)
+  dailyInjectGasTime: number // 注气时间(H)
+  dailyInjectWaterTime: number // 注水时间(H)
+  dailyPowerUsage: number // 日耗电量(度)
+  dailyOilUsage: number // 日耗油量(升)
+  nonProductionTime: number // 非生产时间(小时)
+  nptReason: string // 非生产时间原因
+  constructionStartDate: number // 施工开始日期
+  constructionEndDate: number // 施工结束日期
+  productionStatus: string // 生产动态
+  constructionStatus: string // 施工状态
+  totalGasInjection: number // 注气量(万方)
+  totalWaterInjection: number // 注水量(方)
+  cumulativeCompletion: number // 完工井次
+  capacity: number // 产能(万方)
+  remark: string // 备注
+  auditStatus: number // 审核状态
+  status: number // 状态
+  opinion: string // 审核意见
+}
+
+interface Column {
+  prop?: keyof List
+  label: string
+  'min-width'?: string
+  isTag?: boolean
+  fixed?: 'left' | 'right'
+  formatter?: (row: List) => any
+  children?: Column[]
+  dictType?: string
+}
+
+const columns = ref<Column[]>([
+  {
+    label: '日期',
+    prop: 'createTime',
+    'min-width': '120px',
+    fixed: 'left',
+    formatter: (row: List) => dayjs(row.createTime).format('YYYY-MM-DD')
+  },
+  {
+    label: '施工队伍',
+    prop: 'deptName',
+    fixed: 'left',
+    'min-width': '120px'
+  },
+  {
+    label: '任务',
+    prop: 'taskName',
+    fixed: 'left',
+    'min-width': '120px'
+  },
+  {
+    label: '施工状态',
+    prop: 'constructionStatus',
+    fixed: 'left',
+    'min-width': '120px',
+    isTag: true,
+    dictType: DICT_TYPE.PMS_PROJECT_TASK_SCHEDULE
+  },
+  {
+    label: '审批状态',
+    prop: 'auditStatus',
+    'min-width': '120px',
+    isTag: true,
+    formatter: (row: List) => {
+      switch (row.auditStatus) {
+        case 0:
+          return '待提交'
+        case 10:
+          return '待审批'
+        case 20:
+          return '审批通过'
+        case 30:
+          return '审批拒绝'
+        default:
+          return ''
+      }
+    }
+  },
+  {
+    label: '搬迁安装天数',
+    prop: 'relocationDays',
+    'min-width': '120px',
+    formatter: (row: List) => (row.relocationDays < 0 ? '0' : String(row.relocationDays))
+  },
+  {
+    label: '设计注气量(万方)',
+    prop: 'designInjection',
+    'min-width': '120px',
+    formatter: (row: List) => row.designInjection || '0'
+  },
+  {
+    label: '运行时效',
+    prop: 'transitTime',
+    'min-width': '120px',
+    formatter: (row: List) => (row.transitTime * 100).toFixed(2) + '%'
+  },
+  {
+    label: '当日',
+    children: [
+      {
+        label: '注气量(万方)',
+        prop: 'dailyGasInjection',
+        'min-width': '120px',
+        formatter: (row: List) => (row.dailyGasInjection / 10000).toFixed(2)
+      },
+      {
+        label: '注水量(方)',
+        prop: 'dailyWaterInjection',
+        'min-width': '120px'
+      },
+      {
+        label: '注气时间(H)',
+        prop: 'dailyInjectGasTime',
+        'min-width': '120px'
+      },
+      {
+        label: '注水时间(H)',
+        prop: 'dailyInjectWaterTime',
+        'min-width': '120px'
+      },
+      {
+        label: '用电量(kWh)',
+        prop: 'dailyPowerUsage',
+        'min-width': '120px'
+      },
+      {
+        label: '油耗(L)',
+        prop: 'dailyOilUsage',
+        'min-width': '120px'
+      }
+    ]
+  },
+  {
+    label: '非生产时间(H)',
+    prop: 'nonProductionTime',
+    'min-width': '120px'
+  },
+  {
+    label: '非生产时间原因',
+    prop: 'nptReason',
+    'min-width': '120px',
+    isTag: true,
+    dictType: DICT_TYPE.PMS_PROJECT_NPT_REASON
+  },
+  // {
+  //   label: '施工开始日期',
+  //   prop: 'constructionStartDate',
+  //   'min-width': '120px',
+  //   formatter: (row: List) => dayjs(row.constructionStartDate).format('YYYY-MM-DD HH:mm:ss')
+  // },
+  // {
+  //   label: '施工结束日期',
+  //   prop: 'constructionEndDate',
+  //   'min-width': '120px',
+  //   formatter: (row: List) => dayjs(row.constructionEndDate).format('YYYY-MM-DD HH:mm:ss')
+  // },
+  {
+    label: '生产动态',
+    prop: 'productionStatus',
+    'min-width': '120px'
+  },
+  {
+    label: '项目',
+    prop: 'contractName',
+    'min-width': '120px'
+  },
+  {
+    label: '累计',
+    children: [
+      {
+        label: '注气量(万方)',
+        prop: 'totalGasInjection',
+        'min-width': '120px',
+        formatter: (row: List) => (row.totalGasInjection / 10000).toFixed(2)
+      },
+      {
+        label: '注水量(方)',
+        prop: 'totalWaterInjection',
+        'min-width': '120px'
+      },
+      {
+        label: '完工井次',
+        prop: 'cumulativeCompletion',
+        'min-width': '120px'
+      }
+    ]
+  },
+  {
+    label: '产能(万方)',
+    prop: 'capacity',
+    'min-width': '120px',
+    formatter: (row: List) => (row.capacity / 10000).toFixed(2)
+  }
+])
+
+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 calculateColumnWidths = (colums: Column[]) => {
+  for (const col of colums) {
+    let { formatter, prop, label, 'min-width': minWidth, isTag, children } = col
+
+    if (children && children.length > 0) {
+      calculateColumnWidths(children)
+      continue
+    }
+
+    minWidth =
+      Math.min(
+        ...[
+          Math.max(
+            ...[
+              getTextWidth(label),
+              ...list.value.map((v) => {
+                return getTextWidth(formatter ? formatter(v) : v[prop!])
+              })
+            ]
+          ) + (isTag ? 40 : 20),
+          200
+        ]
+      ) + 'px'
+
+    col['min-width'] = minWidth
+  }
+}
+
+function checkTimeSumEquals24(row: List) {
+  // 获取三个字段的值,转换为数字,如果为空则视为0
+  const gasTime = row.dailyInjectGasTime || 0
+  const waterTime = row.dailyInjectWaterTime || 0
+  const nonProdTime = row.nonProductionTime || 0
+
+  // 计算总和
+  const sum = gasTime + waterTime + nonProdTime
+
+  // 返回是否等于24(允许一定的浮点数误差)
+  return Math.abs(sum - 24) < 0.01 // 使用0.01作为误差范围
+}
+
+function cellStyle(data: {
+  row: List
+  column: TableColumnCtx<List>
+  rowIndex: number
+  columnIndex: number
+}) {
+  const { row, column } = data
+
+  if (column.property === 'transitTime') {
+    const originalValue = row.transitTime ?? 0
+
+    if (originalValue > 1.2)
+      return {
+        color: 'red',
+        fontWeight: 'bold'
+      }
+  }
+
+  const timeFields = ['dailyInjectGasTime', 'dailyInjectWaterTime', 'nonProductionTime']
+  if (timeFields.includes(column.property)) {
+    if (!checkTimeSumEquals24(row)) {
+      return {
+        color: 'orange',
+        fontWeight: 'bold'
+      }
+    }
+  }
+
+  // 默认返回空对象,不应用特殊样式
+  return {}
+}
+
+const id = useUserStore().getUser.deptId ?? 157
+
+const deptId = id
+
+interface Query {
+  pageNo: number
+  pageSize: number
+  deptId: number
+  contractName?: string
+  taskName?: string
+  createTime: string[]
+}
+
+const query = ref<Query>({
+  pageNo: 1,
+  pageSize: 10,
+  deptId: id,
+  createTime: [
+    ...rangeShortcuts[2].value().map((item) => dayjs(item).format('YYYY-MM-DD HH:mm:ss'))
+  ]
+})
+
+function handleSizeChange(val: number) {
+  query.value.pageSize = val
+  handleQuery()
+}
+
+function handleCurrentChange(val: number) {
+  query.value.pageNo = val
+  loadList()
+}
+
+const loading = ref(false)
+
+const list = ref<List[]>([])
+const total = ref(0)
+
+const loadList = useDebounceFn(async function () {
+  loading.value = true
+  try {
+    const data = await IotRhDailyReportApi.getIotRhDailyReportPage(query.value)
+    list.value = data.list
+    total.value = data.total
+
+    nextTick(() => {
+      calculateColumnWidths(columns.value)
+    })
+  } finally {
+    loading.value = false
+  }
+}, 500)
+
+function handleQuery(setPage = true) {
+  if (setPage) {
+    query.value.pageNo = 1
+  }
+  loadList()
+}
+
+function resetQuery() {
+  query.value = {
+    pageNo: 1,
+    pageSize: 10,
+    deptId: 157,
+    contractName: '',
+    taskName: '',
+    createTime: [
+      ...rangeShortcuts[2].value().map((item) => dayjs(item).format('YYYY-MM-DD HH:mm:ss'))
+    ]
+  }
+  handleQuery()
+}
+
+watch(
+  [
+    () => query.value.createTime,
+    () => query.value.deptId,
+    () => query.value.taskName,
+    () => query.value.contractName
+  ],
+  () => {
+    handleQuery()
+  },
+  { immediate: true }
+)
+</script>
+
+<template>
+  <div
+    class="grid grid-cols-[15%_1fr] grid-rows-[62px_1fr] gap-4 h-[calc(100vh-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]"
+  >
+    <el-form
+      size="default"
+      class="col-span-2 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-segmented class="h-12" v-model="tab" :options="tabOptions" />
+        <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"
+            :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+            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>
+
+    <!-- 第二行左侧:自动落入第 2 行第 1 列 -->
+    <div class="p-4 bg-white dark:bg-[#1d1e1f] shadow rounded-lg h-full">
+      <DeptTreeSelect :top-id="157" :deptId="deptId" v-model="query.deptId" />
+    </div>
+
+    <!-- 第二行右侧:自动落入第 2 行第 2 列 -->
+    <div class="bg-white dark:bg-[#1d1e1f] shadow rounded-lg p-4 flex flex-col">
+      <div class="flex-1 relative">
+        <el-auto-resizer class="absolute">
+          <template #default="{ width, height }">
+            <el-table
+              :data="list"
+              v-loading="loading"
+              stripe
+              class="absolute"
+              :max-height="height"
+              show-overflow-tooltip
+              :width="width"
+              :cell-style="cellStyle"
+              border
+            >
+              <DailyTableColumn :columns="columns" />
+              <el-table-column label="操作" width="120px" align="center" fixed="right">
+                <template #default="{ row }">
+                  <el-button link type="success" v-hasPermi="['pms:iot-rh-daily-report:query']">
+                    查看
+                  </el-button>
+                  <el-button
+                    v-show="row.status === 0"
+                    link
+                    type="primary"
+                    v-hasPermi="['pms:iot-rh-daily-report:create']"
+                  >
+                    编辑
+                  </el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </template>
+        </el-auto-resizer>
+      </div>
+      <div class="h-10 mt-4 flex items-center justify-end">
+        <el-pagination
+          size="default"
+          v-show="total > 0"
+          v-model:current-page="query.pageNo"
+          v-model:page-size="query.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>
+  </div>
+</template>
+
+<style scoped>
+:deep(.el-form-item) {
+  margin-bottom: 0;
+}
+
+.el-segmented {
+  --el-border-radius-base: 8px;
+  --el-segmented-padding: 8px;
+}
+</style>