rdProductionBriefs.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. <script lang="ts" setup>
  2. import { IotRdDailyReportApi } from '@/api/pms/iotrddailyreport'
  3. import dayjs from 'dayjs'
  4. import type { Ref } from 'vue'
  5. interface RdProductionBriefRow {
  6. id?: number
  7. projectName?: string
  8. deptName?: string
  9. rdStatusLabel?: string
  10. taskName?: string
  11. techniqueNames?: string
  12. deviceNames?: string
  13. cumulativeWorkingLayers?: number | null
  14. cumulativeWorkingWell?: number | null
  15. constructionBrief?: string
  16. projectSort?: number | null
  17. teamSort?: number | null
  18. }
  19. interface SpanMethodProps {
  20. rowIndex: number
  21. columnIndex: number
  22. }
  23. const TABLE_HEIGHT = 220
  24. const RD_DEPT_ID = 163
  25. const DEFAULT_DATE = dayjs().subtract(1, 'day').format('YYYY-MM-DD')
  26. const props = withDefaults(
  27. defineProps<{
  28. pageMode?: 'compact' | 'full'
  29. }>(),
  30. {
  31. pageMode: 'compact'
  32. }
  33. )
  34. const selectedDate = ref(DEFAULT_DATE)
  35. const loading = ref(false)
  36. const list = ref<RdProductionBriefRow[]>([])
  37. const kbScale = inject<Ref<number>>('rdKbScale', ref(1))
  38. const tableHeight = computed<number | string>(() =>
  39. props.pageMode === 'full' ? '100%' : Math.round(TABLE_HEIGHT * kbScale.value)
  40. )
  41. const tableData = computed(() => {
  42. return [...list.value].sort((a, b) => {
  43. const projectSort = Number(a.projectSort ?? 9999) - Number(b.projectSort ?? 9999)
  44. if (projectSort !== 0) return projectSort
  45. return Number(a.teamSort ?? 9999) - Number(b.teamSort ?? 9999)
  46. })
  47. })
  48. const projectSpanMap = computed(() =>
  49. createSpanMap(tableData.value, (row) => row.projectName || '-')
  50. )
  51. function normalizeList(res: any): RdProductionBriefRow[] {
  52. if (Array.isArray(res?.list)) return res.list
  53. return []
  54. }
  55. function createSpanMap(
  56. rows: RdProductionBriefRow[],
  57. getKey: (row: RdProductionBriefRow) => string
  58. ) {
  59. const spanMap: number[] = []
  60. rows.forEach((row, index) => {
  61. const key = getKey(row)
  62. if (index > 0 && getKey(rows[index - 1]) === key) {
  63. spanMap[index] = 0
  64. return
  65. }
  66. let span = 1
  67. for (let nextIndex = index + 1; nextIndex < rows.length; nextIndex++) {
  68. if (getKey(rows[nextIndex]) !== key) break
  69. span += 1
  70. }
  71. spanMap[index] = span
  72. })
  73. return spanMap
  74. }
  75. function tableSpanMethod({ rowIndex, columnIndex }: SpanMethodProps) {
  76. if (columnIndex !== 0) {
  77. return {
  78. rowspan: 1,
  79. colspan: 1
  80. }
  81. }
  82. const rowspan = projectSpanMap.value[rowIndex]
  83. return {
  84. rowspan,
  85. colspan: rowspan > 0 ? 1 : 0
  86. }
  87. }
  88. function getCreateTimeRange() {
  89. const date = selectedDate.value || DEFAULT_DATE
  90. return [
  91. dayjs(date).startOf('day').format('YYYY-MM-DD HH:mm:ss'),
  92. dayjs(date).endOf('day').format('YYYY-MM-DD HH:mm:ss')
  93. ]
  94. }
  95. function formatText(value?: string | number | null) {
  96. return value === null || value === undefined || value === '' ? '-' : value
  97. }
  98. function handleDateChange() {
  99. getList()
  100. }
  101. async function getList() {
  102. loading.value = true
  103. try {
  104. const res = await IotRdDailyReportApi.getIotRdDailyReportPage({
  105. deptId: RD_DEPT_ID,
  106. createTime: getCreateTimeRange()
  107. })
  108. list.value = normalizeList(res)
  109. } catch (error) {
  110. console.error('获取瑞都生产日报失败:', error)
  111. list.value = []
  112. } finally {
  113. loading.value = false
  114. }
  115. }
  116. onMounted(() => {
  117. getList()
  118. })
  119. </script>
  120. <template>
  121. <div
  122. class="panel device-list-panel production-brief-panel w-full flex flex-col"
  123. :class="{ 'production-brief-panel--full': props.pageMode === 'full' }">
  124. <div class="panel-title flex items-center justify-between">
  125. <div class="kb-panel-title-text flex items-center">
  126. <div class="icon-decorator">
  127. <span></span>
  128. <span></span>
  129. </div>
  130. 生产日报
  131. </div>
  132. <div class="production-brief-panel__picker">
  133. <el-date-picker
  134. v-model="selectedDate"
  135. value-format="YYYY-MM-DD"
  136. type="date"
  137. placeholder="选择日期"
  138. :clearable="false"
  139. class="production-brief-panel__picker-input"
  140. @change="handleDateChange" />
  141. </div>
  142. </div>
  143. <div class="device-list-panel__body flex-1 min-h-0">
  144. <el-table
  145. v-loading="loading"
  146. :data="tableData"
  147. :height="tableHeight"
  148. :span-method="tableSpanMethod"
  149. class="device-list-table production-brief-table"
  150. :class="{ 'production-brief-table--full': props.pageMode === 'full' }">
  151. <el-table-column prop="projectName" label="项目" min-width="150" align="center">
  152. <template #default="{ row }">
  153. {{ formatText(row.projectName) }}
  154. </template>
  155. </el-table-column>
  156. <el-table-column prop="deptName" label="队伍" min-width="110" align="center">
  157. <template #default="{ row }">
  158. {{ formatText(row.deptName) }}
  159. </template>
  160. </el-table-column>
  161. <el-table-column prop="rdStatusLabel" label="状态" min-width="90" align="center">
  162. <template #default="{ row }">
  163. {{ formatText(row.rdStatusLabel) }}
  164. </template>
  165. </el-table-column>
  166. <el-table-column prop="taskName" label="井号" min-width="120" align="center">
  167. <template #default="{ row }">
  168. {{ formatText(row.taskName) }}
  169. </template>
  170. </el-table-column>
  171. <el-table-column prop="techniqueNames" label="工艺" min-width="130" align="center">
  172. <template #default="{ row }">
  173. {{ formatText(row.techniqueNames) }}
  174. </template>
  175. </el-table-column>
  176. <el-table-column prop="deviceNames" label="使用设备" min-width="170" align="center">
  177. <template #default="{ row }">
  178. {{ formatText(row.deviceNames) }}
  179. </template>
  180. </el-table-column>
  181. <el-table-column
  182. prop="cumulativeWorkingLayers"
  183. label="压裂层数"
  184. min-width="100"
  185. align="center">
  186. <template #default="{ row }">
  187. {{ formatText(row.cumulativeWorkingLayers) }}
  188. </template>
  189. </el-table-column>
  190. <el-table-column
  191. prop="cumulativeWorkingWell"
  192. label="连油井数"
  193. min-width="100"
  194. align="center">
  195. <template #default="{ row }">
  196. {{ formatText(row.cumulativeWorkingWell) }}
  197. </template>
  198. </el-table-column>
  199. <el-table-column prop="constructionBrief" label="施工简要" min-width="220" align="center">
  200. <template #default="{ row }">
  201. {{ formatText(row.constructionBrief) }}
  202. </template>
  203. </el-table-column>
  204. <template #empty>
  205. <div class="h-full min-h-[220px] flex items-center justify-center">
  206. <el-empty description="暂无数据" :image-size="72" />
  207. </div>
  208. </template>
  209. </el-table>
  210. </div>
  211. </div>
  212. </template>
  213. <style lang="scss" scoped>
  214. @import url('@/styles/kb.scss');
  215. .device-list-panel.production-brief-panel--full {
  216. height: 100%;
  217. margin-top: 0;
  218. }
  219. .production-brief-panel__picker {
  220. display: flex;
  221. width: calc(120px * var(--kb-scale, 1));
  222. align-items: center;
  223. }
  224. .production-brief-panel__picker-input {
  225. width: calc(120px * var(--kb-scale, 1)) !important;
  226. :deep(.el-input__wrapper) {
  227. min-height: calc(28px * var(--kb-scale, 1));
  228. padding: 0 calc(10px * var(--kb-scale, 1));
  229. }
  230. :deep(.el-input__inner) {
  231. font-size: calc(12px * var(--kb-scale, 1));
  232. }
  233. :deep(.el-input__prefix-inner),
  234. :deep(.el-input__suffix-inner) {
  235. font-size: calc(14px * var(--kb-scale, 1));
  236. }
  237. }
  238. .production-brief-table {
  239. width: 100%;
  240. :deep(.el-table__header-wrapper th.el-table__cell) {
  241. font-size: calc(16px * var(--kb-scale, 1));
  242. line-height: 1.2;
  243. }
  244. :deep(.el-table__body td.el-table__cell) {
  245. padding: calc(7px * var(--kb-scale, 1)) 0;
  246. font-size: calc(14px * var(--kb-scale, 1));
  247. }
  248. }
  249. .production-brief-table--full {
  250. :deep(.el-scrollbar__view) {
  251. display: block;
  252. height: 100%;
  253. }
  254. :deep(.el-table__body) {
  255. height: 100%;
  256. }
  257. :deep(.el-table__body tbody) {
  258. height: 100%;
  259. }
  260. }
  261. </style>