rdProductionBriefs.vue 9.3 KB

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