task-review.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. <template>
  2. <hc-new-dialog v-model="isShow" is-table widths="96%" title="任务审核" @close="cancelClick">
  3. <template #header="{ titleId, titleClass }">
  4. <div class="hc-card-header flex items-center">
  5. <div :id="titleId" :class="titleClass">任务审核 【已开启电签】</div>
  6. </div>
  7. </template>
  8. <div v-loading="isLoading" class="relative h-full">
  9. <div class="hc-task-name relative">{{ rowInfo.taskName }} 审批信息</div>
  10. <div class="hc-task-body relative flex">
  11. <div class="hc-task-time">
  12. <hc-body class="hc-task-body-card" padding="10px" scrollbar>
  13. <el-timeline class="hc-time-line">
  14. <template v-for="(item, index) in flowList" :key="index">
  15. <el-timeline-item :class="item.status === '2' ? 'success' : 'primary'" size="large">
  16. <div class="timeline-item-icon">
  17. <hc-icon v-if="item.status === '2'" class="check-icon" name="check" />
  18. </div>
  19. <div class="reply-name">{{ item.name }}</div>
  20. <div class="reply-time">{{ item.date }}</div>
  21. <div class="reply-content" v-html="item.flowValue" />
  22. </el-timeline-item>
  23. </template>
  24. </el-timeline>
  25. </hc-body>
  26. </div>
  27. <div :id="`hc_task_table_${uuid}`" class="hc-task-table">
  28. <hc-body class="hc-task-body-card" padding="10px">
  29. <hc-table
  30. ref="tableRef" :column="tableColumn" :datas="tableData" :is-stripe="false"
  31. is-new :index-style="{ width: 60 }" is-current-row @row-click="tableRowClick"
  32. >
  33. <template #action="{ row }">
  34. <div class="hc-task-table-action" :class="row.isComment === 1 ? 'is-cur' : ''" @click="rowRemarkClick(row)">
  35. <i class="i-iconoir-star-solid" />
  36. </div>
  37. </template>
  38. <template #state="{ row }">
  39. <div class="hc-task-table-state">
  40. <i v-if="row.status === 1" class="i-iconoir-check-circle-solid is-success" />
  41. <i v-else-if="row.status === 2" class="i-iconoir-xmark-circle-solid is-danger" />
  42. <span v-else-if="row.status === 3">审批结束</span>
  43. <i v-else class="i-iconoir-help-circle-solid" />
  44. </div>
  45. </template>
  46. </hc-table>
  47. </hc-body>
  48. </div>
  49. <div :id="`hc_task_form_${uuid}`" class="hc-task-form">
  50. <hc-body class="hc-task-body-card" padding="10px" scrollbar>
  51. <HcTaskForm :table="tableInfo" :info="rowInfo" :is-edit="tabsKey === 1" @finish="taskFormFinish" />
  52. </hc-body>
  53. </div>
  54. </div>
  55. </div>
  56. <template #footer>
  57. <div class="hc-task-dialog-footer">
  58. <el-button :disabled="tabsKey !== 1" @click="rejectionClick">驳回审批</el-button>
  59. <el-button type="primary" :loading="confirmLoading" :disabled="tabsKey !== 1" @click="confirmClick">同意审批</el-button>
  60. </div>
  61. </template>
  62. </hc-new-dialog>
  63. <!-- 批注 -->
  64. <HcTaskNotes v-model="isNotesShow" :table="tableNoteInfo" :info="rowInfo" :is-edit="tabsKey === 1" @finish="taskNotesFinish" />
  65. <!-- 驳回 -->
  66. <HcRepealForm v-model="isRepealShow" :info="rowInfo" @finish="taskRepealFinish" />
  67. </template>
  68. <script setup>
  69. import { nextTick, ref, watch } from 'vue'
  70. import { getArrValue, getObjValue, getRandom } from 'js-fast-way'
  71. import { useAppStore } from '~src/store'
  72. import HcTaskForm from './task-form.vue'
  73. import HcTaskNotes from './task-notes.vue'
  74. import HcRepealForm from './repeal-form.vue'
  75. import mainApi from '~api/tasks/hc-data'
  76. const props = defineProps({
  77. tabs: {
  78. type: [String, Number],
  79. default: '',
  80. },
  81. row: {
  82. type: Object,
  83. default: () => ({}),
  84. },
  85. })
  86. //事件
  87. const emit = defineEmits(['finish', 'close'])
  88. const uuid = getRandom(4)
  89. const useAppState = useAppStore()
  90. const projectId = ref(useAppState.getProjectId || '')
  91. const contractId = ref(useAppState.getContractId || '')
  92. //双向绑定
  93. // eslint-disable-next-line no-undef
  94. const isShow = defineModel('modelValue', {
  95. default: false,
  96. })
  97. //监听
  98. const tableRef = ref(null)
  99. const tabsKey = ref(props.tabs)
  100. const rowInfo = ref(props.row)
  101. watch(() => [
  102. props.tabs,
  103. props.row,
  104. ], ([key, row]) => {
  105. tabsKey.value = key
  106. rowInfo.value = row
  107. }, {
  108. immediate: true,
  109. deep: true,
  110. })
  111. //监听显示
  112. watch(isShow, (val) => {
  113. if (val) {
  114. setTaskInfo()
  115. setSplitRef()
  116. }
  117. })
  118. //初始化设置拖动分割线
  119. const setSplitRef = () => {
  120. //配置参考: https://split.js.org/#/?direction=vertical&snapOffset=0
  121. nextTick(() => {
  122. window.$split(['#hc_task_table_' + uuid, '#hc_task_form_' + uuid], {
  123. sizes: [50, 50],
  124. snapOffset: 0,
  125. minSize: [50, 500],
  126. })
  127. })
  128. }
  129. //设置任务信息
  130. const setTaskInfo = () => {
  131. //meterType:1中间,2材料,3开工,4变更令
  132. const { meterType } = rowInfo.value
  133. if (meterType === 1) {
  134. tableColumn.value = middlepayTableColumn.value
  135. } else if (meterType === 2) {
  136. tableColumn.value = materialTableColumn.value
  137. } else if (meterType === 3) {
  138. tableColumn.value = startWorkTableColumn.value
  139. } else if (meterType === 4) {
  140. tableColumn.value = alterTableColumn.value
  141. } else {
  142. tableColumn.value = []
  143. }
  144. getTableDetail()
  145. }
  146. //获取数据详情
  147. const isLoading = ref(false)
  148. const getTableDetail = async () => {
  149. isLoading.value = true
  150. confirmLoading.value = true
  151. //获取数据
  152. const { data } = await mainApi.getDetail(rowInfo.value.id)
  153. const { taskProcessInfo, taskCenterDataInfo } = getObjValue(data)
  154. tableData.value = getArrValue(taskCenterDataInfo)
  155. flowList.value = getArrValue(taskProcessInfo)
  156. //默认选中第一行
  157. let info = {}
  158. if (tableData.value.length > 0) {
  159. info = tableData.value[0]
  160. }
  161. await nextTick(() => {
  162. tableInfo.value = info
  163. tableRef.value?.tableRef?.setCurrentRow(info)
  164. })
  165. //关闭加载状态
  166. isLoading.value = false
  167. confirmLoading.value = false
  168. }
  169. //流程信息,1待审批,2已审批
  170. const flowList = ref([])
  171. //中间计量单的表格数据
  172. const middlepayTableColumn = ref([
  173. { key: 'action', name: '批注', width: 45, align: 'center' },
  174. { key: 'meterNumber', name: '计量单编号' },
  175. { key: 'meterMoney', name: '计量金额', width: 100 },
  176. { key: 'engineerDivide', name: '工程划分' },
  177. { key: 'state', name: '审批状态', fixed: 'right', width: 70, align: 'center' },
  178. ])
  179. //开工预付款计量单的表格数据
  180. const startWorkTableColumn = ref([
  181. { key: 'action', name: '批注', width: 45, align: 'center' },
  182. { key: 'periodName', name: '计量期', minWidth: 100, align: 'center' },
  183. { key: 'businessDate', name: '业务日期', width: 160, align: 'center' },
  184. { key: 'meterMoney', name: '计量金额', width: 100, align: 'center' },
  185. { key: 'state', name: '审批状态', fixed: 'right', width: 70, align: 'center' },
  186. ])
  187. //变更令的表格数据
  188. const alterTableColumn = ref([
  189. { key: 'action', name: '批注', width: 45, align: 'center' },
  190. { key: 'changeNumber', name: '变更编号', minWidth: 120, align: 'center' },
  191. { key: 'changeName', name: '变更名称', minWidth: 120, align: 'center' },
  192. { key: 'changeMoney', name: '变更金额', width: 100, align: 'center' },
  193. { key: 'changeApprovalDate', name: '变更批复日期', width: 160, align: 'center' },
  194. { key: 'state', name: '审批状态', fixed: 'right', width: 70, align: 'center' },
  195. ])
  196. //材料计量单的表格数据
  197. const materialTableColumn = ref([
  198. { key: 'action', name: '批注', width: 45, align: 'center' },
  199. { key: 'periodName', name: '计量期', minWidth: 100, align: 'center' },
  200. { key: 'contractMaterialName', name: '合同材料', minWidth: 120, align: 'center' },
  201. { key: 'materialArriveNumber', name: '材料到场编号', width: 120, align: 'center' },
  202. { key: 'meterMoney', name: '计量金额', width: 100, align: 'center' },
  203. { key: 'state', name: '审批状态', fixed: 'right', width: 70, align: 'center' },
  204. ])
  205. //表格数据
  206. const tableColumn = ref([])
  207. const tableData = ref([])
  208. //表格行被点击
  209. const tableInfo = ref({})
  210. const tableRowClick = ({ row }) => {
  211. tableInfo.value = row
  212. }
  213. //批注, isComment 是否已批注,1=是,0=否
  214. const isNotesShow = ref(false)
  215. const tableNoteInfo = ref({})
  216. const rowRemarkClick = (row) => {
  217. tableNoteInfo.value = row
  218. isNotesShow.value = true
  219. }
  220. //批注完成
  221. const taskNotesFinish = () => {
  222. getTableDetail()
  223. }
  224. //单条审批
  225. const taskFormFinish = () => {
  226. getTableDetail()
  227. }
  228. //确认审批
  229. const confirmLoading = ref(false)
  230. const confirmClick = async () => {
  231. confirmLoading.value = true
  232. const { error, code, msg } = await mainApi.taskApprove({
  233. taskId: rowInfo.value.id,
  234. projectId: projectId.value,
  235. contractId: contractId.value,
  236. })
  237. confirmLoading.value = false
  238. if (!error && code === 200) {
  239. window.$message.success('审批成功')
  240. emit('finish')
  241. } else {
  242. window.$message.error(msg ?? '审批失败')
  243. }
  244. }
  245. //驳回审批
  246. const isRepealShow = ref(false)
  247. const rejectionClick = async () => {
  248. isRepealShow.value = true
  249. }
  250. //驳回完成
  251. const taskRepealFinish = () => {
  252. getTableDetail()
  253. }
  254. //取消审批
  255. const cancelClick = () => {
  256. isShow.value = false
  257. isLoading.value = false
  258. confirmLoading.value = false
  259. tableColumn.value = []
  260. tableData.value = []
  261. tableInfo.value = {}
  262. emit('close')
  263. }
  264. </script>
  265. <style lang="scss" scoped>
  266. .hc-task-name {
  267. font-weight: bold;
  268. color: #1A1a1a;
  269. padding-bottom: 10px;
  270. border-bottom: 1px solid #f5f5f5;
  271. }
  272. .hc-task-body {
  273. height: calc(100% - 27px);
  274. .hc-task-time {
  275. position: relative;
  276. height: 100%;
  277. flex-shrink: 0;
  278. width: 170px;
  279. }
  280. .hc-task-table, .hc-task-form {
  281. position: relative;
  282. height: 100%;
  283. flex: 1;
  284. flex-basis: auto;
  285. }
  286. .hc-task-table {
  287. border-left: 1px solid #e5e5e5;
  288. }
  289. }
  290. //表格图标
  291. .hc-task-table-action, .hc-task-table-state {
  292. position: relative;
  293. display: flex;
  294. justify-content: center;
  295. align-items: center;
  296. cursor: pointer;
  297. font-size: 20px;
  298. color: #929293;
  299. i {
  300. display: inline-flex;
  301. }
  302. }
  303. //表格批注
  304. .hc-task-table-action.is-cur {
  305. color: #F2B90B;
  306. }
  307. //表格状态
  308. .hc-task-table-state {
  309. .is-success {
  310. color: #25a62d;
  311. }
  312. .is-danger {
  313. color: #F5221D;
  314. }
  315. span {
  316. color: #1A1a1a;
  317. }
  318. }
  319. //弹窗底部
  320. .hc-task-dialog-footer {
  321. position: relative;
  322. text-align: center;
  323. }
  324. </style>
  325. <style lang="scss">
  326. .hc-task-body-card {
  327. background: #f7f7f7;
  328. .el-scrollbar__bar.is-vertical {
  329. right: -8px;
  330. }
  331. }
  332. </style>