approval.vue 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908
  1. <script lang="ts" setup>
  2. import { IotRhDailyReportApi } from '@/api/pms/iotrhdailyreport'
  3. import { rangeShortcuts } from '@/utils/formatTime'
  4. import { useDebounceFn } from '@vueuse/core'
  5. import dayjs from 'dayjs'
  6. import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
  7. import { TableColumnCtx } from 'element-plus/es/components/table/src/table-column/defaults'
  8. import { FormInstance, FormRules } from 'element-plus'
  9. import Form from '@/components/Form/src/Form.vue'
  10. import { useUserStore } from '@/store/modules/user'
  11. interface List {
  12. createTime: number // 日期
  13. deptName: string // 施工队伍
  14. contractName: string // 项目
  15. taskName: string // 任务
  16. id: number
  17. deptId: number
  18. projectId: number
  19. taskId: number
  20. relocationDays: number // 搬迁安装天数
  21. designInjection: string // 设计注气量(万方)
  22. transitTime: number // 运行时效
  23. dailyGasInjection: number // 注气量(万方)
  24. dailyWaterInjection: number // 注水量(方)
  25. dailyInjectGasTime: number // 注气时间(H)
  26. dailyInjectWaterTime: number // 注水时间(H)
  27. dailyPowerUsage: number // 日耗电量(度)
  28. dailyOilUsage: number // 日耗油量(升)
  29. nonProductionTime: number // 非生产时间(小时)
  30. nptReason: string // 非生产时间原因
  31. constructionStartDate: number // 施工开始日期
  32. constructionEndDate: number // 施工结束日期
  33. productionStatus: string // 生产动态
  34. constructionStatus: string // 施工状态
  35. totalGasInjection: number // 注气量(万方)
  36. totalWaterInjection: number // 注水量(方)
  37. cumulativeCompletion: number // 完工井次
  38. capacity: number // 产能(万方)
  39. remark: string // 备注
  40. auditStatus: number // 审核状态
  41. status: number // 状态
  42. opinion: string // 审核意见
  43. }
  44. interface Column {
  45. prop?: keyof List
  46. label: string
  47. 'min-width'?: string
  48. isTag?: boolean
  49. formatter?: (row: List) => any
  50. children?: Column[]
  51. dictType?: string
  52. }
  53. const columns = ref<Column[]>([
  54. {
  55. label: '日期',
  56. prop: 'createTime',
  57. 'min-width': '120px',
  58. formatter: (row: List) => dayjs(row.createTime).format('YYYY-MM-DD')
  59. },
  60. {
  61. label: '施工队伍',
  62. prop: 'deptName',
  63. 'min-width': '120px'
  64. },
  65. {
  66. label: '项目',
  67. prop: 'contractName',
  68. 'min-width': '120px'
  69. },
  70. {
  71. label: '任务',
  72. prop: 'taskName',
  73. 'min-width': '120px'
  74. },
  75. {
  76. label: '施工状态',
  77. prop: 'constructionStatus',
  78. 'min-width': '120px',
  79. isTag: true,
  80. dictType: DICT_TYPE.PMS_PROJECT_TASK_SCHEDULE
  81. },
  82. {
  83. label: '审批状态',
  84. prop: 'auditStatus',
  85. 'min-width': '120px',
  86. isTag: true,
  87. formatter: (row: List) => {
  88. switch (row.auditStatus) {
  89. case 0:
  90. return '待提交'
  91. case 10:
  92. return '待审批'
  93. case 20:
  94. return '审批通过'
  95. case 30:
  96. return '审批拒绝'
  97. default:
  98. return ''
  99. }
  100. }
  101. }
  102. // {
  103. // label: '搬迁安装天数',
  104. // prop: 'relocationDays',
  105. // 'min-width': '120px',
  106. // formatter: (row: List) => (row.relocationDays < 0 ? '0' : String(row.relocationDays))
  107. // },
  108. // {
  109. // label: '设计注气量(万方)',
  110. // prop: 'designInjection',
  111. // 'min-width': '120px',
  112. // formatter: (row: List) => row.designInjection || '0'
  113. // },
  114. // {
  115. // label: '运行时效',
  116. // prop: 'transitTime',
  117. // 'min-width': '120px',
  118. // formatter: (row: List) => (row.transitTime * 100).toFixed(2) + '%'
  119. // },
  120. // {
  121. // label: '当日',
  122. // children: [
  123. // {
  124. // label: '注气量(万方)',
  125. // prop: 'dailyGasInjection',
  126. // 'min-width': '120px',
  127. // formatter: (row: List) => (row.dailyGasInjection / 10000).toFixed(2)
  128. // },
  129. // {
  130. // label: '注水量(方)',
  131. // prop: 'dailyWaterInjection',
  132. // 'min-width': '120px'
  133. // },
  134. // {
  135. // label: '注气时间(H)',
  136. // prop: 'dailyInjectGasTime',
  137. // 'min-width': '120px'
  138. // },
  139. // {
  140. // label: '注水时间(H)',
  141. // prop: 'dailyInjectWaterTime',
  142. // 'min-width': '120px'
  143. // },
  144. // {
  145. // label: '用电量(kWh)',
  146. // prop: 'dailyPowerUsage',
  147. // 'min-width': '120px'
  148. // },
  149. // {
  150. // label: '油耗(L)',
  151. // prop: 'dailyOilUsage',
  152. // 'min-width': '120px'
  153. // }
  154. // ]
  155. // },
  156. // {
  157. // label: '非生产时间(H)',
  158. // prop: 'nonProductionTime',
  159. // 'min-width': '120px'
  160. // },
  161. // {
  162. // label: '非生产时间原因',
  163. // prop: 'nptReason',
  164. // 'min-width': '120px',
  165. // isTag: true,
  166. // dictType: DICT_TYPE.PMS_PROJECT_NPT_REASON
  167. // },
  168. // {
  169. // label: '施工开始日期',
  170. // prop: 'constructionStartDate',
  171. // 'min-width': '120px',
  172. // formatter: (row: List) => dayjs(row.constructionStartDate).format('YYYY-MM-DD HH:mm:ss')
  173. // },
  174. // {
  175. // label: '施工结束日期',
  176. // prop: 'constructionEndDate',
  177. // 'min-width': '120px',
  178. // formatter: (row: List) => dayjs(row.constructionEndDate).format('YYYY-MM-DD HH:mm:ss')
  179. // },
  180. // {
  181. // label: '生产动态',
  182. // prop: 'productionStatus',
  183. // 'min-width': '120px'
  184. // },
  185. // {
  186. // label: '累计',
  187. // children: [
  188. // {
  189. // label: '注气量(万方)',
  190. // prop: 'totalGasInjection',
  191. // 'min-width': '120px',
  192. // formatter: (row: List) => (row.totalGasInjection / 10000).toFixed(2)
  193. // },
  194. // {
  195. // label: '注水量(方)',
  196. // prop: 'totalWaterInjection',
  197. // 'min-width': '120px'
  198. // },
  199. // {
  200. // label: '完工井次',
  201. // prop: 'cumulativeCompletion',
  202. // 'min-width': '120px'
  203. // }
  204. // ]
  205. // },
  206. // {
  207. // label: '产能(万方)',
  208. // prop: 'capacity',
  209. // 'min-width': '120px',
  210. // formatter: (row: List) => (row.capacity / 10000).toFixed(2)
  211. // }
  212. ])
  213. const getTextWidth = (text: string, fontSize = 12) => {
  214. const span = document.createElement('span')
  215. span.style.visibility = 'hidden'
  216. span.style.position = 'absolute'
  217. span.style.whiteSpace = 'nowrap'
  218. span.style.fontSize = `${fontSize}px`
  219. span.style.fontFamily = 'PingFang SC'
  220. span.innerText = text
  221. document.body.appendChild(span)
  222. const width = span.offsetWidth
  223. document.body.removeChild(span)
  224. return width
  225. }
  226. const calculateColumnWidths = (colums: Column[]) => {
  227. for (const col of colums) {
  228. let { formatter, prop, label, 'min-width': minWidth, isTag, children } = col
  229. if (children && children.length > 0) {
  230. calculateColumnWidths(children)
  231. continue
  232. }
  233. minWidth =
  234. Math.min(
  235. ...[
  236. Math.max(
  237. ...[
  238. getTextWidth(label),
  239. ...list.value.map((v) => {
  240. return getTextWidth(formatter ? formatter(v) : v[prop!])
  241. })
  242. ]
  243. ) + (isTag ? 40 : 20),
  244. 200
  245. ]
  246. ) + 'px'
  247. col['min-width'] = minWidth
  248. }
  249. }
  250. function checkTimeSumEquals24(row: List) {
  251. // 获取三个字段的值,转换为数字,如果为空则视为0
  252. const gasTime = row.dailyInjectGasTime || 0
  253. const waterTime = row.dailyInjectWaterTime || 0
  254. const nonProdTime = row.nonProductionTime || 0
  255. // 计算总和
  256. const sum = gasTime + waterTime + nonProdTime
  257. // 返回是否等于24(允许一定的浮点数误差)
  258. return Math.abs(sum - 24) < 0.01 // 使用0.01作为误差范围
  259. }
  260. function cellStyle(data: {
  261. row: List
  262. column: TableColumnCtx<List>
  263. rowIndex: number
  264. columnIndex: number
  265. }) {
  266. const { row, column } = data
  267. if (column.property === 'transitTime') {
  268. const originalValue = row.transitTime ?? 0
  269. if (originalValue > 1.2)
  270. return {
  271. color: 'red',
  272. fontWeight: 'bold'
  273. }
  274. }
  275. const timeFields = ['dailyInjectGasTime', 'dailyInjectWaterTime', 'nonProductionTime']
  276. if (timeFields.includes(column.property)) {
  277. if (!checkTimeSumEquals24(row)) {
  278. return {
  279. color: 'orange',
  280. fontWeight: 'bold'
  281. }
  282. }
  283. }
  284. // 默认返回空对象,不应用特殊样式
  285. return {}
  286. }
  287. const id = useUserStore().getUser.deptId
  288. const deptId = id
  289. interface Query {
  290. pageNo: number
  291. pageSize: number
  292. deptId: number
  293. contractName?: string
  294. taskName?: string
  295. createTime: string[]
  296. }
  297. const query = ref<Query>({
  298. pageNo: 1,
  299. pageSize: 10,
  300. deptId: id,
  301. createTime: [
  302. ...rangeShortcuts[2].value().map((item) => dayjs(item).format('YYYY-MM-DD HH:mm:ss'))
  303. ]
  304. })
  305. function handleSizeChange(val: number) {
  306. query.value.pageSize = val
  307. handleQuery()
  308. }
  309. function handleCurrentChange(val: number) {
  310. query.value.pageNo = val
  311. loadList()
  312. }
  313. const loading = ref(false)
  314. const list = ref<List[]>([])
  315. const total = ref(0)
  316. const loadList = useDebounceFn(async function () {
  317. loading.value = true
  318. try {
  319. const data = await IotRhDailyReportApi.getIotRhDailyReportPage(query.value)
  320. list.value = data.list
  321. total.value = data.total
  322. nextTick(() => {
  323. calculateColumnWidths(columns.value)
  324. })
  325. } finally {
  326. loading.value = false
  327. }
  328. }, 500)
  329. function handleQuery(setPage = true) {
  330. if (setPage) {
  331. query.value.pageNo = 1
  332. }
  333. loadList()
  334. }
  335. function resetQuery() {
  336. query.value = {
  337. pageNo: 1,
  338. pageSize: 10,
  339. deptId: 157,
  340. contractName: '',
  341. taskName: '',
  342. createTime: [
  343. ...rangeShortcuts[2].value().map((item) => dayjs(item).format('YYYY-MM-DD HH:mm:ss'))
  344. ]
  345. }
  346. handleQuery()
  347. }
  348. watch(
  349. [
  350. () => query.value.createTime,
  351. () => query.value.deptId,
  352. () => query.value.taskName,
  353. () => query.value.contractName
  354. ],
  355. () => {
  356. handleQuery()
  357. },
  358. { immediate: true }
  359. )
  360. const FORM_KEYS = [
  361. 'id',
  362. 'deptName',
  363. 'contractName',
  364. 'taskName',
  365. 'dailyGasInjection',
  366. 'dailyWaterInjection',
  367. 'dailyInjectGasTime',
  368. 'dailyInjectWaterTime',
  369. 'nonProductionTime',
  370. 'nptReason',
  371. 'productionStatus',
  372. 'remark',
  373. 'relocationDays',
  374. 'capacity',
  375. 'createTime',
  376. 'deptId',
  377. 'projectId',
  378. 'taskId',
  379. 'auditStatus',
  380. 'opinion'
  381. ] as const
  382. type FormKey = (typeof FORM_KEYS)[number]
  383. type Form = Partial<Pick<List, FormKey>>
  384. const dialogVisible = ref(false)
  385. const formRef = ref<FormInstance>()
  386. const formLoading = ref(false)
  387. const message = useMessage()
  388. const initFormData = (): Form => ({
  389. dailyGasInjection: 0,
  390. dailyWaterInjection: 0,
  391. dailyInjectGasTime: 0,
  392. dailyInjectWaterTime: 0,
  393. nonProductionTime: 0,
  394. relocationDays: 0,
  395. capacity: 0
  396. })
  397. const form = ref<Form>(initFormData())
  398. async function loadDetail(id: number) {
  399. try {
  400. const res = await IotRhDailyReportApi.getIotRhDailyReport(id)
  401. FORM_KEYS.forEach((key) => {
  402. form.value[key] = res[key] ?? form.value[key]
  403. })
  404. form.value.id = id
  405. if (res.auditStatus !== 10) {
  406. formType.value = 'readonly'
  407. }
  408. // if (!form.value.capacity) {
  409. // message.error('请维护增压机产能')
  410. // }
  411. } finally {
  412. }
  413. }
  414. const formType = ref<'approval' | 'readonly'>('approval')
  415. function handleOpenForm(id: number, type: 'approval' | 'readonly') {
  416. form.value = initFormData()
  417. formRef.value?.resetFields()
  418. formType.value = type
  419. dialogVisible.value = true
  420. loadDetail(id).then(() => {
  421. formRef.value?.validate()
  422. })
  423. }
  424. const route = useRoute()
  425. onMounted(() => {
  426. if (Object.keys(route.query).length > 0) {
  427. handleOpenForm(Number(route.query.id), 'approval')
  428. }
  429. })
  430. const transitTime = computed(() => {
  431. const cap = form.value.capacity
  432. const gas = form.value.dailyGasInjection ?? 0
  433. if (!cap) return { original: 0, value: '0%' }
  434. const original = gas / cap
  435. return { original, value: (original * 100).toFixed(2) + '%' }
  436. })
  437. const sumTimes = () => {
  438. const { dailyInjectGasTime = 0, dailyInjectWaterTime = 0, nonProductionTime = 0 } = form.value
  439. return parseFloat((dailyInjectGasTime + dailyInjectWaterTime + nonProductionTime).toFixed(2))
  440. }
  441. const validateTotalTime = (_rule: any, _value: any, callback: any) => {
  442. const total = sumTimes()
  443. if (total !== 24) {
  444. callback(new Error(`当前合计 ${total} 小时,三项时间之和必须等于 24`))
  445. } else {
  446. callback()
  447. }
  448. }
  449. const validateNptReason = (_rule: any, value: any, callback: any) => {
  450. if ((form.value.nonProductionTime || 0) > 0 && !value) {
  451. callback(new Error('非生产时间大于 0 时,必须选择原因'))
  452. } else {
  453. callback()
  454. }
  455. }
  456. const timeRuleItem = [
  457. { required: true, message: '请输入时间', trigger: 'blur' },
  458. { validator: validateTotalTime, trigger: 'blur' }
  459. ]
  460. const rules = reactive<FormRules>({
  461. dailyGasInjection: [{ required: true, message: '请输入当日注气量', trigger: 'blur' }],
  462. dailyWaterInjection: [{ required: true, message: '请输入当日注水量', trigger: 'blur' }],
  463. productionStatus: [{ required: true, message: '请输入生产动态', trigger: 'blur' }],
  464. // 复用规则
  465. dailyInjectGasTime: timeRuleItem,
  466. dailyInjectWaterTime: timeRuleItem,
  467. nonProductionTime: timeRuleItem,
  468. nptReason: [{ validator: validateNptReason, trigger: ['change', 'blur'] }]
  469. })
  470. watch(
  471. [
  472. () => form.value.dailyInjectGasTime,
  473. () => form.value.dailyInjectWaterTime,
  474. () => form.value.nonProductionTime
  475. ],
  476. () => {
  477. nextTick(() => {
  478. formRef.value?.validateField('nptReason')
  479. if (sumTimes() === 24) {
  480. formRef.value?.clearValidate([
  481. 'dailyInjectGasTime',
  482. 'dailyInjectWaterTime',
  483. 'nonProductionTime'
  484. ])
  485. }
  486. })
  487. }
  488. )
  489. const submitForm = async (auditStatus: 20 | 30) => {
  490. if (!formRef.value) return
  491. try {
  492. // await formRef.value.validate()
  493. formLoading.value = true
  494. const { opinion, id } = form.value
  495. const data = { id: id, auditStatus, opinion } as any
  496. await IotRhDailyReportApi.approvalIotRhDailyReport(data)
  497. message.success(auditStatus === 20 ? '通过成功' : '拒绝成功')
  498. dialogVisible.value = false
  499. loadList()
  500. } catch (error) {
  501. console.warn('表单校验未通过或提交出错')
  502. } finally {
  503. formLoading.value = false
  504. }
  505. }
  506. </script>
  507. <template>
  508. <div class="grid grid-cols-[15%_1fr] gap-4 h-full">
  509. <div class="flex flex-col p-4 gap-2 bg-white dark:bg-[#1d1e1f] shadow rounded-lg h-full">
  510. <DeptTreeSelect :deptId="deptId" :topId="157" v-model="query.deptId" />
  511. </div>
  512. <div class="grid grid-rows-[62px_1fr] h-full gap-4">
  513. <el-form
  514. size="default"
  515. class="bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-8 gap-8 flex items-center justify-between"
  516. >
  517. <div class="flex items-center gap-8">
  518. <el-form-item label="项目">
  519. <el-input
  520. v-model="query.contractName"
  521. placeholder="请输入项目"
  522. clearable
  523. @keyup.enter="handleQuery()"
  524. class="!w-240px"
  525. />
  526. </el-form-item>
  527. <el-form-item label="任务">
  528. <el-input
  529. v-model="query.taskName"
  530. placeholder="请输入任务"
  531. clearable
  532. @keyup.enter="handleQuery()"
  533. class="!w-240px"
  534. />
  535. </el-form-item>
  536. <el-form-item label="创建时间">
  537. <el-date-picker
  538. v-model="query.createTime"
  539. value-format="YYYY-MM-DD HH:mm:ss"
  540. type="daterange"
  541. start-placeholder="开始日期"
  542. end-placeholder="结束日期"
  543. :shortcuts="rangeShortcuts"
  544. class="!w-220px"
  545. :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
  546. />
  547. </el-form-item>
  548. </div>
  549. <el-form-item>
  550. <el-button type="primary" @click="handleQuery()">
  551. <Icon icon="ep:search" class="mr-5px" /> 搜索
  552. </el-button>
  553. <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" />重置</el-button>
  554. </el-form-item>
  555. </el-form>
  556. <div class="bg-white dark:bg-[#1d1e1f] shadow rounded-lg p-4 flex flex-col">
  557. <div class="flex-1 relative">
  558. <el-auto-resizer class="absolute">
  559. <template #default="{ width, height }">
  560. <el-table
  561. :data="list"
  562. v-loading="loading"
  563. stripe
  564. class="absolute"
  565. :max-height="height"
  566. show-overflow-tooltip
  567. :width="width"
  568. :cell-style="cellStyle"
  569. border
  570. >
  571. <DailyTableColumn :columns="columns" />
  572. <el-table-column label="操作" width="120px" align="center" fixed="right">
  573. <template #default="{ row }">
  574. <el-button
  575. link
  576. type="success"
  577. @click="handleOpenForm(row.id, 'readonly')"
  578. v-hasPermi="['pms:iot-rh-daily-report:update']"
  579. >
  580. 查看
  581. </el-button>
  582. <el-button
  583. v-show="row.auditStatus === 10"
  584. link
  585. type="primary"
  586. @click="handleOpenForm(row.id, 'approval')"
  587. v-hasPermi="['pms:iot-rh-daily-report:update']"
  588. >
  589. 审批
  590. </el-button>
  591. </template>
  592. </el-table-column>
  593. </el-table>
  594. </template>
  595. </el-auto-resizer>
  596. </div>
  597. <div class="h-10 mt-4 flex items-center justify-end">
  598. <el-pagination
  599. size="default"
  600. v-show="total > 0"
  601. v-model:current-page="query.pageNo"
  602. v-model:page-size="query.pageSize"
  603. :background="true"
  604. :page-sizes="[10, 20, 30, 50, 100]"
  605. :total="total"
  606. layout="total, sizes, prev, pager, next, jumper"
  607. @size-change="handleSizeChange"
  608. @current-change="handleCurrentChange"
  609. />
  610. </div>
  611. </div>
  612. </div>
  613. </div>
  614. <Dialog title="编辑" v-model="dialogVisible">
  615. <el-form
  616. ref="formRef"
  617. label-position="top"
  618. size="default"
  619. :rules="rules"
  620. :model="form"
  621. v-loading="formLoading"
  622. require-asterisk-position="right"
  623. >
  624. <div class="flex flex-col gap-3 text-sm">
  625. <div
  626. class="rounded-md border border-blue-100 bg-blue-50/80 p-3 dark:border-blue-900/30 dark:bg-blue-900/10"
  627. >
  628. <div class="flex flex-col gap-2.5">
  629. <div class="flex items-center justify-between">
  630. <div class="text-gray-600 dark:text-gray-400">
  631. <span class="font-bold text-gray-800 dark:text-gray-200">运行时效:</span>
  632. 当日注气量 / 产能
  633. </div>
  634. <span
  635. class="inline-flex items-center rounded border border-red-200 bg-red-100 px-2 py-0.5 text-xs font-medium text-red-600 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400"
  636. >
  637. >120% 红色预警
  638. </span>
  639. </div>
  640. <div class="flex items-center justify-between">
  641. <div class="text-gray-600 dark:text-gray-400">
  642. <span class="font-bold text-gray-800 dark:text-gray-200">时间平衡:</span>
  643. 注气 + 注水 + 非生产 = 24H
  644. </div>
  645. <span
  646. class="inline-flex items-center rounded border border-orange-200 bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-600 dark:bg-orange-900/20 dark:border-orange-800 dark:text-orange-400"
  647. >
  648. ≠24H 橙色预警
  649. </span>
  650. </div>
  651. </div>
  652. </div>
  653. <!-- <div
  654. v-if="form.opinion"
  655. class="flex gap-3 rounded-md border border-yellow-200 bg-yellow-50 p-3 dark:border-yellow-800 dark:bg-yellow-900/10"
  656. >
  657. <Icon
  658. icon="ep:warning-filled"
  659. class="mt-0.5 shrink-0 text-base text-yellow-600 dark:text-yellow-500"
  660. />
  661. <div class="flex flex-col">
  662. <h4 class="mb-1 font-bold text-yellow-800 dark:text-yellow-500"> 审核意见 </h4>
  663. <p class="leading-relaxed text-gray-600 dark:text-gray-400">
  664. {{ form.opinion }}
  665. </p>
  666. </div>
  667. </div> -->
  668. </div>
  669. <div class="grid grid-cols-2 gap-4 mt-5">
  670. <el-form-item label="施工队伍" prop="deptName">
  671. <el-input v-model="form.deptName" placeholder="" disabled />
  672. </el-form-item>
  673. <el-form-item label="项目" prop="contractName">
  674. <el-input v-model="form.contractName" placeholder="" disabled />
  675. </el-form-item>
  676. <el-form-item label="任务" prop="taskName">
  677. <el-input v-model="form.taskName" placeholder="" disabled />
  678. </el-form-item>
  679. <el-form-item label="搬迁安装天数(D)" prop="relocationDays">
  680. <el-input v-model="form.relocationDays" placeholder="" disabled />
  681. </el-form-item>
  682. <el-form-item label="运行时效" prop="transitTime">
  683. <el-input
  684. :model-value="transitTime.value"
  685. placeholder="运行时效"
  686. disabled
  687. :class="{ 'warning-input': transitTime.original > 1.2 }"
  688. id="transitTimeInput"
  689. />
  690. </el-form-item>
  691. <el-form-item label="当日注气量(方)" prop="dailyGasInjection">
  692. <el-input-number
  693. class="w-full!"
  694. :min="0"
  695. v-model="form.dailyGasInjection"
  696. placeholder="请输入当日注气量(方)"
  697. disabled
  698. />
  699. </el-form-item>
  700. <el-form-item label="当日注水量(方)" prop="dailyWaterInjection">
  701. <el-input-number
  702. class="w-full!"
  703. :min="0"
  704. v-model="form.dailyWaterInjection"
  705. placeholder="请输入当日注水量(方)"
  706. disabled
  707. />
  708. </el-form-item>
  709. <el-form-item label="当日注气时间(H)" prop="dailyInjectGasTime">
  710. <el-input-number
  711. class="w-full!"
  712. :min="0"
  713. v-model="form.dailyInjectGasTime"
  714. placeholder="请输入当日注气时间(H)"
  715. disabled
  716. :class="{ 'orange-input': sumTimes() !== 24 }"
  717. />
  718. </el-form-item>
  719. <el-form-item label="当日注水时间(H)" prop="dailyInjectWaterTime">
  720. <el-input-number
  721. class="w-full!"
  722. :min="0"
  723. v-model="form.dailyInjectWaterTime"
  724. placeholder="当日注水时间(H)"
  725. disabled
  726. :class="{ 'orange-input': sumTimes() !== 24 }"
  727. />
  728. </el-form-item>
  729. <el-form-item label="非生产时间(H)" prop="nonProductionTime">
  730. <el-input-number
  731. class="w-full!"
  732. :min="0"
  733. v-model="form.nonProductionTime"
  734. placeholder="非生产时间(H)"
  735. disabled
  736. :class="{ 'orange-input': sumTimes() !== 24 }"
  737. />
  738. </el-form-item>
  739. <el-form-item label="非生产时间原因" prop="nptReason">
  740. <el-select v-model="form.nptReason" placeholder="请选择" disabled clearable>
  741. <el-option
  742. v-for="(dict, index) of getStrDictOptions(DICT_TYPE.PMS_PROJECT_NPT_REASON)"
  743. :key="index"
  744. :label="dict.label"
  745. :value="dict.value"
  746. />
  747. </el-select>
  748. </el-form-item>
  749. <div class="grid grid-cols-1 gap-4 mt-5">
  750. <el-form-item label="生产动态" prop="productionStatus">
  751. <el-input
  752. v-model="form.productionStatus"
  753. placeholder="请输入生产动态"
  754. type="textarea"
  755. autosize
  756. :max-length="1000"
  757. disabled
  758. />
  759. </el-form-item>
  760. <el-form-item label="备注" prop="remark">
  761. <el-input
  762. v-model="form.remark"
  763. placeholder="请输入备注"
  764. :max-length="1000"
  765. type="textarea"
  766. autosize
  767. disabled
  768. />
  769. </el-form-item>
  770. </div>
  771. </div>
  772. <el-form-item class="mt-4" label="审批意见" prop="opinion">
  773. <el-input
  774. v-model="form.opinion"
  775. placeholder="请输入审批意见"
  776. :max-length="1000"
  777. type="textarea"
  778. autosize
  779. :disabled="formType === 'readonly'"
  780. />
  781. </el-form-item>
  782. </el-form>
  783. <template #footer>
  784. <el-button
  785. size="default"
  786. @click="submitForm(20)"
  787. type="primary"
  788. :disabled="formLoading || formType === 'readonly'"
  789. >
  790. 审批通过
  791. </el-button>
  792. <el-button
  793. size="default"
  794. @click="submitForm(30)"
  795. type="danger"
  796. :disabled="formLoading || formType === 'readonly'"
  797. >
  798. 审批拒绝
  799. </el-button>
  800. <el-button size="default" @click="dialogVisible = false">取 消</el-button>
  801. </template>
  802. </Dialog>
  803. </template>
  804. <style scoped>
  805. :deep(.el-form-item) {
  806. margin-bottom: 0;
  807. }
  808. :deep(.el-table) {
  809. border-top-right-radius: 8px;
  810. border-top-left-radius: 8px;
  811. .el-table__cell {
  812. height: 40px;
  813. }
  814. .el-table__header-wrapper {
  815. .el-table__cell {
  816. background: var(--el-fill-color-light);
  817. }
  818. }
  819. }
  820. :deep(.warning-input) {
  821. .el-input__inner {
  822. color: red !important;
  823. -webkit-text-fill-color: red !important;
  824. }
  825. }
  826. :deep(.orange-input) {
  827. .el-input__inner {
  828. color: orange !important;
  829. -webkit-text-fill-color: orange !important;
  830. }
  831. }
  832. :deep(.el-input-number__decrease) {
  833. display: none !important;
  834. }
  835. :deep(.el-input-number__increase) {
  836. display: none !important;
  837. }
  838. </style>