|
@@ -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>
|