Explorar el Código

5#国日报汇总

yanghao hace 6 días
padre
commit
c03aaf04f1

+ 1 - 1
.env.local

@@ -4,7 +4,7 @@ NODE_ENV=development
 VITE_DEV=true
 
 # 请求路径  http://192.168.188.200:48080  https://iot.deepoil.cc:5443  http://172.26.0.56:48080
-VITE_BASE_URL='https://iot.deepoil.cc:5443'
+VITE_BASE_URL='http://192.168.188.198:48080'
 
 # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
 VITE_UPLOAD_TYPE=server

+ 5 - 0
src/api/pms/fiveconstructiondailyreport/index.ts

@@ -12,5 +12,10 @@ export const FiveIotProjectDailyReportApi = {
   // 填报项目日报
   updateIotProjectDailyReport: async (data) => {
     return await request.put({ url: '/pms/iot-five-daily-report/update', data })
+  },
+
+  // 日报汇总列表
+  getIotProjectDailyReportSummary: async (params) => {
+    return await request.get({ url: 'pms/iot-five-daily-report/totalWorkload', params })
   }
 }

+ 4 - 4
src/views/pms/constructionDailyReport/components/report-config.ts

@@ -70,8 +70,8 @@ export const tableColumns: Array<{
   fixed?: 'left' | 'right'
 }> = [
   { prop: 'reportName', label: '日报名称', minWidth: 180, fixed: 'left' },
-  { prop: 'createTime', label: '创建(施工)日期', width: 120 },
-  { prop: 'constructionEndDate', label: '施工结束日期', width: 120 },
+  { prop: 'createTime', label: '创建日期', width: 120 },
+  // { prop: 'constructionEndDate', label: '施工结束日期', width: 120 },
   { prop: 'projectName', label: '项目', minWidth: 160, fixed: 'left' },
   { prop: 'location', label: '施工区域', minWidth: 140 },
   { prop: 'deptName', label: '队伍', minWidth: 140 },
@@ -81,7 +81,7 @@ export const tableColumns: Array<{
   { prop: 'fiveStatusLabel', label: '队伍状态', minWidth: 120 },
   { prop: 'dailyWorkingLayers', label: '当日主压层数', width: 120 },
   { prop: 'dailySandVolume', label: '当日加砂量(吨)', width: 130 },
-  { prop: 'dailyLiquidVolume', label: '当日注液量(m3)', width: 130 },
+  { prop: 'dailyLiquidVolume', label: '当日注液量()', width: 130 },
   { prop: 'monthWorkingWells', label: '当月作业井数', width: 120 },
   { prop: 'monthWorkingLayers', label: '当月作业层数', width: 120 },
   { prop: 'dailyFuel', label: '当日油耗(L)', width: 110 },
@@ -135,7 +135,7 @@ export const formSections: ConstructionDailyReportSection[] = [
       },
       {
         prop: 'dailyLiquidVolume',
-        label: '当日注液量()',
+        label: '当日注液量()',
         type: 'number',
         editableInEdit: true
       },

+ 385 - 0
src/views/pms/fiveDailyReportSummary/index.vue

@@ -0,0 +1,385 @@
+<script setup lang="ts">
+import dayjs from 'dayjs'
+import CountTo from '@/components/count-to1.vue'
+import { FiveIotProjectDailyReportApi } from '@/api/pms/fiveconstructiondailyreport'
+import { useUserStore } from '@/store/modules/user'
+import { rangeShortcuts } from '@/utils/formatTime'
+import { useDebounceFn } from '@vueuse/core'
+import ConstructionDailyReportTable from '../constructionDailyReport/components/ConstructionDailyReportTable.vue'
+import type {
+  ConstructionDailyReportQuery,
+  ConstructionDailyReportRow
+} from '../constructionDailyReport/components/report-config'
+
+defineOptions({ name: 'FiveDailyReportSummary' })
+
+type SummaryCardKey = 'dailyWorkingLayers' | 'dailyFuel' | 'dailySandVolume' | 'dailyLiquidVolume' | 'utilizationRate'
+
+interface SummaryCard {
+  key: SummaryCardKey
+  label: string
+  unit: string
+  icon: string
+  decimals?: number
+}
+
+const router = useRouter()
+const route = useRoute()
+const userStore = useUserStore()
+const deptId = userStore.getUser.deptId
+
+const summaryCards: SummaryCard[] = [
+  {
+    key: 'dailyWorkingLayers',
+    label: '压裂',
+    unit: '层',
+    icon: 'i-material-symbols-layers-outline-rounded',
+    decimals: 0
+  },
+  {
+    key: 'dailyFuel',
+    label: '油耗',
+    unit: 'L',
+    icon: 'i-material-symbols-local-gas-station-outline-rounded',
+    decimals: 0
+  },
+  {
+    key: 'dailySandVolume',
+    label: '加砂',
+    unit: '方',
+    icon: 'i-material-symbols-grain-outline-rounded',
+    decimals: 0
+  },
+  {
+    key: 'dailyLiquidVolume',
+    label: '注液',
+    unit: '方',
+    icon: 'i-material-symbols-water-drop-outline-rounded',
+    decimals: 0
+  },
+  {
+    key: 'utilizationRate',
+    label: '设备利用率',
+    unit: '%',
+    icon: 'i-material-symbols-monitoring-outline-rounded',
+    decimals: 2
+  }
+]
+
+const cardMockData = ref<Record<SummaryCardKey, number>>({
+  dailyWorkingLayers: 128,
+  dailyFuel: 18650,
+  dailySandVolume: 920,
+  dailyLiquidVolume: 4830,
+  utilizationRate: 87.36
+})
+
+const initQuery = (): ConstructionDailyReportQuery => ({
+  pageNo: 1,
+  pageSize: 10,
+  deptId,
+  projectName: '',
+  wellName: '',
+  createTime: [
+    ...rangeShortcuts[1].value().map((item) => dayjs(item).format('YYYY-MM-DD HH:mm:ss'))
+  ]
+})
+
+const query = ref<ConstructionDailyReportQuery>(initQuery())
+const list = ref<ConstructionDailyReportRow[]>([])
+const total = ref(0)
+const loading = ref(false)
+const currentDeptName = ref('5号国')
+
+const loadList = useDebounceFn(async () => {
+  loading.value = true
+  try {
+    const data = await FiveIotProjectDailyReportApi.getIotProjectDailyReportPage(query.value as any)
+    list.value = data?.list ?? []
+    total.value = data?.total ?? 0
+  } finally {
+    loading.value = false
+  }
+}, 200)
+
+function handleQuery(resetPage = true) {
+  if (resetPage) {
+    query.value.pageNo = 1
+  }
+  loadList()
+}
+
+function resetQuery() {
+  query.value = initQuery()
+  currentDeptName.value = '5号国'
+  handleQuery()
+}
+
+function handleDeptNodeClick(node: { name?: string }) {
+  currentDeptName.value = node?.name || '5号国'
+  handleQuery()
+}
+
+function handleSizeChange(value: number) {
+  query.value.pageSize = value
+  handleQuery()
+}
+
+function handleCurrentChange(value: number) {
+  query.value.pageNo = value
+  loadList()
+}
+
+function openReport(row: ConstructionDailyReportRow) {
+  if (!row.id) return
+
+  router.push({
+    name: 'ConstructionDailyReport',
+    query: {
+      id: row.id
+    }
+  })
+}
+
+function syncQueryFromRoute() {
+  const createTime = route.query.createTime
+
+  if (Array.isArray(createTime)) {
+    const values = createTime.filter((item): item is string => typeof item === 'string')
+    if (values.length === 2) {
+      query.value.createTime = values
+    }
+  } else if (typeof createTime === 'string') {
+    const values = createTime.split(',').filter(Boolean)
+    if (values.length === 2) {
+      query.value.createTime = values
+    }
+  }
+}
+
+syncQueryFromRoute()
+
+watch(
+  [
+    () => query.value.deptId,
+    () => query.value.projectName,
+    () => query.value.wellName,
+    () => query.value.createTime?.[0],
+    () => query.value.createTime?.[1]
+  ],
+  () => handleQuery(),
+  { immediate: true }
+)
+
+watch(
+  () => route.query.createTime,
+  () => {
+    syncQueryFromRoute()
+  }
+)
+</script>
+
+<template>
+  <div
+    class="grid grid-cols-[auto_1fr] grid-rows-[104px_62px_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]">
+    <DeptTreeSelect
+      v-model="query.deptId"
+      :top-id="388"
+      :deptId="deptId"
+      :show-title="false"
+      class="row-span-3"
+      @node-click="handleDeptNodeClick" />
+
+    <div class="grid grid-cols-5 gap-4">
+      <article
+        v-for="card in summaryCards"
+        :key="card.key"
+        class="summary-card bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-5 py-4 min-w-0">
+        <div class="summary-card__icon" :class="card.icon"></div>
+        <div class="min-w-0">
+          <div class="summary-card__label">{{ card.label }}({{ card.unit }})</div>
+          <count-to
+            class="summary-card__value"
+            :start-val="0"
+            :end-val="cardMockData[card.key]"
+            :decimals="card.decimals || 0">
+            <span class="summary-card__placeholder">--</span>
+          </count-to>
+        </div>
+      </article>
+    </div>
+
+    <el-form
+      size="default"
+      class="summary-query-form bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between">
+      <div class="flex items-center gap-8 min-w-0">
+        <el-form-item label="项目">
+          <el-input
+            v-model="query.projectName"
+            placeholder="请输入项目名称"
+            clearable
+            @keyup.enter="handleQuery()"
+            class="!w-240px" />
+        </el-form-item>
+        <el-form-item label="生产任务">
+          <el-input
+            v-model="query.wellName"
+            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-260px" />
+        </el-form-item>
+      </div>
+      <el-form-item class="summary-query-actions">
+        <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="summary-content min-h-0">
+      <div class="summary-content__header">
+        <h3 class="summary-content__title">{{ currentDeptName }}日报汇总</h3>
+        <span class="summary-content__tip">统计卡片当前为模拟数据,表格为实际日报数据</span>
+      </div>
+
+      <ConstructionDailyReportTable
+        :list="list"
+        :loading="loading"
+        :total="total"
+        :page-no="query.pageNo"
+        :page-size="query.pageSize"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange">
+        <template #action="{ row }">
+          <el-button link type="primary" @click="openReport(row)">查看日报</el-button>
+        </template>
+      </ConstructionDailyReportTable>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+:deep(.el-form-item) {
+  margin-bottom: 0;
+}
+
+.summary-card {
+  position: relative;
+  display: flex;
+  align-items: center;
+  gap: 14px;
+  overflow: hidden;
+}
+
+.summary-card::after {
+  content: '';
+  position: absolute;
+  inset: auto -32px -32px auto;
+  width: 96px;
+  height: 96px;
+  border-radius: 999px;
+  background: linear-gradient(135deg, rgb(59 130 246 / 0.14), rgb(16 185 129 / 0.08));
+}
+
+.summary-card__icon {
+  position: relative;
+  z-index: 1;
+  flex: none;
+  width: 44px;
+  height: 44px;
+  border-radius: 14px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 24px;
+  color: #2563eb;
+  background: linear-gradient(135deg, rgb(37 99 235 / 0.14), rgb(14 165 233 / 0.08));
+}
+
+.summary-card__label {
+  position: relative;
+  z-index: 1;
+  color: var(--el-text-color-regular);
+  font-size: 13px;
+  line-height: 20px;
+  margin-bottom: 4px;
+}
+
+.summary-card__value {
+  position: relative;
+  z-index: 1;
+  font-size: 28px;
+  font-weight: 600;
+  line-height: 1;
+  color: var(--el-text-color-primary);
+}
+
+.summary-card__placeholder {
+  font-size: 16px;
+  color: var(--el-text-color-placeholder);
+}
+
+.summary-query-form {
+  min-height: 62px;
+}
+
+.summary-query-actions :deep(.el-form-item__content) {
+  flex-wrap: nowrap;
+  gap: 8px;
+}
+
+.summary-query-actions :deep(.el-button + .el-button) {
+  margin-left: 0;
+}
+
+.summary-content {
+  background: #fff;
+  border-radius: 12px;
+  box-shadow: var(--el-box-shadow-light);
+  padding: 16px;
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.summary-content__header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px;
+}
+
+.summary-content__title {
+  font-size: 18px;
+  line-height: 28px;
+  font-weight: 600;
+  color: var(--el-text-color-primary);
+}
+
+.summary-content__tip {
+  font-size: 12px;
+  line-height: 18px;
+  color: var(--el-text-color-regular);
+}
+
+@media (max-width: 1600px) {
+  .summary-card__value {
+    font-size: 24px;
+  }
+}
+</style>