|
@@ -0,0 +1,251 @@
|
|
|
|
|
+<script lang="ts" setup>
|
|
|
|
|
+import { IotStatApi } from '@/api/pms/stat'
|
|
|
|
|
+import { rangeShortcuts } from '@/utils/formatTime'
|
|
|
|
|
+import dayjs from 'dayjs'
|
|
|
|
|
+
|
|
|
|
|
+interface RyProductionBriefRow {
|
|
|
|
|
+ id: number
|
|
|
|
|
+ projectClassification: string
|
|
|
|
|
+ projectName: string
|
|
|
|
|
+ deptName: string
|
|
|
|
|
+ taskName: string
|
|
|
|
|
+ constructionStatusName: string
|
|
|
|
|
+ dailyFootage: number | null
|
|
|
|
|
+ completedWells: number | null
|
|
|
|
|
+ dailyPowerUsage: number | null
|
|
|
|
|
+ dailyFuel: number | null
|
|
|
|
|
+ nonProductionTime: number | null
|
|
|
|
|
+ nextPlan: string
|
|
|
|
|
+ constructionBrief: string
|
|
|
|
|
+ projectSort?: number | null
|
|
|
|
|
+ teamSort?: number | null
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface SpanMethodProps {
|
|
|
|
|
+ rowIndex: number
|
|
|
|
|
+ columnIndex: number
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const TABLE_HEIGHT = 220
|
|
|
|
|
+const MERGE_COLUMN_INDEXES = [0, 1]
|
|
|
|
|
+const COMPANY_ORDER = ['钻井', '修井']
|
|
|
|
|
+const DEFAULT_TIME_RANGE = rangeShortcuts[2]
|
|
|
|
|
+ .value()
|
|
|
|
|
+ .map((item) => dayjs(item).format('YYYY-MM-DD HH:mm:ss'))
|
|
|
|
|
+
|
|
|
|
|
+const createTime = ref<string[]>(DEFAULT_TIME_RANGE)
|
|
|
|
|
+const loading = ref(false)
|
|
|
|
|
+const list = ref<RyProductionBriefRow[]>([])
|
|
|
|
|
+
|
|
|
|
|
+const tableData = computed(() => {
|
|
|
|
|
+ return [...list.value].sort((a, b) => {
|
|
|
|
|
+ const companySort =
|
|
|
|
|
+ getCompanyOrder(a.projectClassification) - getCompanyOrder(b.projectClassification)
|
|
|
|
|
+ if (companySort !== 0) return companySort
|
|
|
|
|
+
|
|
|
|
|
+ const projectSort = Number(a.projectSort ?? 9999) - Number(b.projectSort ?? 9999)
|
|
|
|
|
+ if (projectSort !== 0) return projectSort
|
|
|
|
|
+
|
|
|
|
|
+ const projectNameSort = (a.projectName || '').localeCompare(b.projectName || '', 'zh-Hans-CN')
|
|
|
|
|
+ if (projectNameSort !== 0) return projectNameSort
|
|
|
|
|
+
|
|
|
|
|
+ return Number(a.teamSort ?? 9999) - Number(b.teamSort ?? 9999)
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const spanMaps = computed(() => {
|
|
|
|
|
+ return {
|
|
|
|
|
+ company: createSpanMap(tableData.value, (row) => row.projectClassification || '-'),
|
|
|
|
|
+ project: createSpanMap(
|
|
|
|
|
+ tableData.value,
|
|
|
|
|
+ (row) => `${row.projectClassification || '-'}__${row.projectName || '-'}`
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+function getCompanyOrder(value?: string) {
|
|
|
|
|
+ const index = COMPANY_ORDER.indexOf(value || '')
|
|
|
|
|
+ return index === -1 ? COMPANY_ORDER.length : index
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function createSpanMap(
|
|
|
|
|
+ rows: RyProductionBriefRow[],
|
|
|
|
|
+ getKey: (row: RyProductionBriefRow) => string
|
|
|
|
|
+) {
|
|
|
|
|
+ const spanMap: number[] = []
|
|
|
|
|
+
|
|
|
|
|
+ rows.forEach((row, index) => {
|
|
|
|
|
+ const key = getKey(row)
|
|
|
|
|
+
|
|
|
|
|
+ if (index > 0 && getKey(rows[index - 1]) === key) {
|
|
|
|
|
+ spanMap[index] = 0
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let span = 1
|
|
|
|
|
+ for (let nextIndex = index + 1; nextIndex < rows.length; nextIndex++) {
|
|
|
|
|
+ if (getKey(rows[nextIndex]) !== key) break
|
|
|
|
|
+ span += 1
|
|
|
|
|
+ }
|
|
|
|
|
+ spanMap[index] = span
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ return spanMap
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function tableSpanMethod({ rowIndex, columnIndex }: SpanMethodProps) {
|
|
|
|
|
+ if (!MERGE_COLUMN_INDEXES.includes(columnIndex)) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ rowspan: 1,
|
|
|
|
|
+ colspan: 1
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const rowspan =
|
|
|
|
|
+ columnIndex === 0 ? spanMaps.value.company[rowIndex] : spanMaps.value.project[rowIndex]
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ rowspan,
|
|
|
|
|
+ colspan: rowspan > 0 ? 1 : 0
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function formatNumber(value?: number | null, fractionDigits = 1) {
|
|
|
|
|
+ const numberValue = Number(value ?? 0)
|
|
|
|
|
+ return Number.isInteger(numberValue) ? `${numberValue}` : numberValue.toFixed(fractionDigits)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function formatFootageOrWell(row: RyProductionBriefRow) {
|
|
|
|
|
+ if (row.projectClassification === '钻井') {
|
|
|
|
|
+ return `${formatNumber(row.dailyFootage)}m`
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return formatNumber(row.completedWells, 0)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function normalizeList(res: any): RyProductionBriefRow[] {
|
|
|
|
|
+ if (Array.isArray(res)) return res
|
|
|
|
|
+ if (Array.isArray(res?.list)) return res.list
|
|
|
|
|
+ if (Array.isArray(res?.records)) return res.records
|
|
|
|
|
+ if (Array.isArray(res?.data)) return res.data
|
|
|
|
|
+ return []
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function handleDateChange() {
|
|
|
|
|
+ getList()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function getList() {
|
|
|
|
|
+ loading.value = true
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await IotStatApi.getRyProductionBriefs({ createTime: createTime.value })
|
|
|
|
|
+ list.value = normalizeList(res).filter((row) =>
|
|
|
|
|
+ COMPANY_ORDER.includes(row.projectClassification || '')
|
|
|
|
|
+ )
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('获取瑞鹰生产简报失败:', error)
|
|
|
|
|
+ list.value = []
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ loading.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ getList()
|
|
|
|
|
+})
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="panel w-full h-[280px] flex flex-col mt-3">
|
|
|
|
|
+ <div class="panel-title h-9 flex items-center justify-between">
|
|
|
|
|
+ <div class="flex items-center">
|
|
|
|
|
+ <div class="icon-decorator">
|
|
|
|
|
+ <span></span>
|
|
|
|
|
+ <span></span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ 生产简报
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="w-260px! -translate-y-[4px]">
|
|
|
|
|
+ <el-date-picker
|
|
|
|
|
+ v-model="createTime"
|
|
|
|
|
+ value-format="YYYY-MM-DD HH:mm:ss"
|
|
|
|
|
+ type="daterange"
|
|
|
|
|
+ start-placeholder="开始日期"
|
|
|
|
|
+ end-placeholder="结束日期"
|
|
|
|
|
+ :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
|
|
|
|
+ :clearable="false"
|
|
|
|
|
+ class="w-260px!"
|
|
|
|
|
+ @change="handleDateChange" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="flex-1 min-h-0 px-4 py-2">
|
|
|
|
|
+ <el-table
|
|
|
|
|
+ v-loading="loading"
|
|
|
|
|
+ :data="tableData"
|
|
|
|
|
+ :height="TABLE_HEIGHT"
|
|
|
|
|
+ :span-method="tableSpanMethod"
|
|
|
|
|
+ class="device-list-table production-brief-table">
|
|
|
|
|
+ <el-table-column prop="projectClassification" label="公司" min-width="72" align="center" />
|
|
|
|
|
+ <el-table-column prop="projectName" label="项目" min-width="150" align="center" />
|
|
|
|
|
+ <el-table-column prop="deptName" label="队伍" min-width="94" align="center" />
|
|
|
|
|
+ <el-table-column prop="taskName" label="生产任务" min-width="130" align="center" />
|
|
|
|
|
+ <el-table-column
|
|
|
|
|
+ prop="constructionStatusName"
|
|
|
|
|
+ label="运行状态"
|
|
|
|
|
+ min-width="88"
|
|
|
|
|
+ align="center" />
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column prop="nextPlan" label="下步任务" min-width="130" align="center" />
|
|
|
|
|
+ <el-table-column
|
|
|
|
|
+ prop="constructionBrief"
|
|
|
|
|
+ label="当日生产简况"
|
|
|
|
|
+ min-width="160"
|
|
|
|
|
+ align="center" />
|
|
|
|
|
+
|
|
|
|
|
+ <el-table-column label="当日进尺(m)/当日井次" min-width="150" align="center">
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ {{ formatFootageOrWell(row) }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="当日电耗(kwh)" min-width="120" align="center">
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ {{ formatNumber(row.dailyPowerUsage) }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="当日油耗(升)" min-width="112" align="center">
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ {{ formatNumber(row.dailyFuel) }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="当日非生产时间" min-width="126" align="center">
|
|
|
|
|
+ <template #default="{ row }">
|
|
|
|
|
+ {{ formatNumber(row.nonProductionTime) }}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+
|
|
|
|
|
+ <template #empty>
|
|
|
|
|
+ <div class="h-full min-h-[220px] flex items-center justify-center">
|
|
|
|
|
+ <el-empty description="暂无数据" :image-size="72" />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<style lang="scss" scoped>
|
|
|
|
|
+@import url('@/styles/kb.scss');
|
|
|
|
|
+
|
|
|
|
|
+.production-brief-table {
|
|
|
|
|
+ :deep(.el-table__header-wrapper th.el-table__cell) {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ line-height: 1.2;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.el-table__body td.el-table__cell) {
|
|
|
|
|
+ padding: 7px 0;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|