index.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. <template>
  2. <div class="mainOfficeCenter">
  3. <div class="header">
  4. <el-popover placement="bottom-start" title="" trigger="hover" width="auto">
  5. <template #reference>
  6. <el-button type="danger" color="#eb6237" style="color: #fff">
  7. <i class="fa fa-plus-circle" style="margin-right: 5px"></i>新建流程
  8. </el-button>
  9. </template>
  10. <ul class="flowTemplateBox">
  11. <template v-for="(item, index) in flowTemplateTree" :key="index">
  12. <li v-if="item['children'].length > 0">
  13. <span class="title">{{ item['name'] }}</span>
  14. <ul v-for="(cItem, cIndex) in item['children']" :key="cIndex">
  15. <li @click="addTProcessHandle(cItem)">{{ cItem['name'] }}</li>
  16. </ul>
  17. </li>
  18. </template>
  19. </ul>
  20. </el-popover>
  21. <ul class="tabs">
  22. <li
  23. v-for="item in tabList"
  24. :key="item['key']"
  25. :class="{ active: currentTab === item['key'] }"
  26. @click="switchTabHandle(item)"
  27. >
  28. {{ item['name'] }}({{ item['value'] }})
  29. </li>
  30. </ul>
  31. <div class="search">
  32. <el-form :inline="true" :model="searchForm" class="demo-form-inline">
  33. <el-form-item label="流程名称">
  34. <el-tree-select
  35. v-model="flowTemIds"
  36. multiple
  37. node-key="id"
  38. collapse-tags
  39. collapse-tags-tooltip
  40. :data="flowTemplates"
  41. :props="defaultProps"
  42. :render-after-expand="false"
  43. ref="treeRef"
  44. style="width: 240px"
  45. default-expand-all
  46. show-checkbox
  47. />
  48. </el-form-item>
  49. <el-form-item label="">
  50. <el-input
  51. type="text"
  52. style="width: 280px"
  53. v-model="sendData['searchVal']"
  54. @keydown="keydownHandle"
  55. placeholder="业务编号/流程描述/流程名称"
  56. />
  57. </el-form-item>
  58. <el-form-item>
  59. <el-button type="primary" @click="searchHandle" color="#2e77e6" style="color: #fff">
  60. 查询
  61. </el-button>
  62. </el-form-item>
  63. </el-form>
  64. </div>
  65. </div>
  66. <div
  67. class="table_box"
  68. v-for="(tabItem, tabIndex) in tabList"
  69. v-show="currentTab === tabItem['key']"
  70. :key="tabIndex"
  71. >
  72. <div class="table">
  73. <el-table
  74. :data="tabTableMapping[tabItem['key']]['data']"
  75. :style="{ width: '100%' }"
  76. stripe
  77. :header-cell-style="{
  78. background: '#E5F0FB',
  79. color: '#233755',
  80. height: '50px'
  81. }"
  82. :highlight-current-row="true"
  83. :cell-class-name="cellClassNameHandle"
  84. @row-dblclick="(item) => describtionHandle(item, currentTab)"
  85. >
  86. <el-table-column
  87. v-for="(item, index) in tabTableMapping[tabItem['key']]['column']"
  88. :key="index"
  89. :label="item['label']"
  90. :prop="item['name']"
  91. :width="item['width']"
  92. :min-width="item['minWidth']"
  93. :fixed="item['fixed']"
  94. :formatter="item['formatter']"
  95. :show-overflow-tooltip="item['tooltip']"
  96. >
  97. <template v-if="item['name'] === 'DESCRIBTION'" #default="scope">
  98. <span class="text_hover" @click="describtionHandle(scope.row, currentTab)">
  99. {{ scope.row[item['name']] }}
  100. </span>
  101. </template>
  102. <template v-else-if="item['name'] === 'statusVal'" #default="scope">
  103. <span v-html="scope.row[item['name']]"></span>
  104. </template>
  105. <template v-else-if="item['name'] === 'action'" #default="scope">
  106. <template v-if="['90', '99', '40', '20', '160', '210'].includes(currentTab)">
  107. <el-button
  108. type="primary"
  109. color="#2e77e6"
  110. style="color: #fff"
  111. @click="openFlowHandle(scope.row, currentTab)"
  112. >
  113. <i class="fa fa-search" style="margin-right: 5px"></i> 查看
  114. </el-button>
  115. </template>
  116. <template v-if="currentTab === '1'">
  117. <el-dropdown
  118. split-button
  119. type="primary"
  120. trigger="click"
  121. @click="flowApplyHandle(scope.row)"
  122. @command="
  123. (name) => {
  124. commandHandle(name, scope.row)
  125. }
  126. "
  127. >
  128. <i class="fa fa-pencil" style="margin-right: 5px"></i>办理
  129. <template #dropdown>
  130. <el-dropdown-menu>
  131. <el-dropdown-item
  132. v-for="(bItem, bIndex) in scope.row.btnArrJson"
  133. :key="bIndex"
  134. :disabled="!bItem['iEnable']"
  135. :command="bItem['value']"
  136. >{{ bItem['value'] }}</el-dropdown-item
  137. >
  138. </el-dropdown-menu>
  139. </template>
  140. </el-dropdown>
  141. </template>
  142. <template v-else-if="currentTab === '90'">
  143. <el-popconfirm title="是否确定追回?" @confirm="recoverActivityHandle(scope.row)">
  144. <template #reference>
  145. <el-button type="warning" color="#e07541" style="color: #fff">
  146. <i class="fa fa-gavel" style="margin-right: 5px"></i> 追回
  147. </el-button>
  148. </template>
  149. </el-popconfirm>
  150. </template>
  151. <template v-else-if="currentTab === '20'">
  152. <el-popconfirm title="是否确定解挂?" @confirm="addIHangUpHandle(scope.row)">
  153. <template #reference>
  154. <el-button type="warning" icon="RefreshRight"> 解挂 </el-button>
  155. </template>
  156. </el-popconfirm>
  157. </template>
  158. <template v-else-if="currentTab === '160'">
  159. <el-button
  160. type="warning"
  161. @click="recoverINullyApplyHandle(scope.row)"
  162. color="#f08551"
  163. style="color: #fff"
  164. >
  165. <i class="fa fa-reply" style="margin-right: 5px"></i>恢复
  166. </el-button>
  167. <el-popconfirm
  168. title="是否确定彻底作废?"
  169. @confirm="completelyVoidINullyApplyHandle(scope.row)"
  170. >
  171. <template #reference>
  172. <el-button type="warning" color="#ec5b57" style="color: #fff">
  173. <i class="fa fa-trash-o" style="margin-right: 5px"></i>彻底作废
  174. </el-button>
  175. </template>
  176. </el-popconfirm>
  177. </template>
  178. </template>
  179. </el-table-column>
  180. </el-table>
  181. </div>
  182. <div class="pageBox">
  183. <el-pagination
  184. v-model:current-page="tabTableMapping[tabItem['key']]['page']"
  185. :page-size="tabTableMapping[tabItem['key']]['rows']"
  186. background
  187. layout="total, prev, pager, next, jumper"
  188. :total="tabTableMapping[tabItem['key']]['records']"
  189. @current-change="handleCurrentChange"
  190. />
  191. </div>
  192. </div>
  193. <!-- 作废表单Dialog -->
  194. <el-dialog v-model="nullyApplyVisible" :title="nullyApplyTitle" width="40%">
  195. <el-form v-model="inullyApplyForm" label-width="100px">
  196. <el-row :gutter="20">
  197. <el-col :span="12">
  198. <el-form-item label="申请人:">
  199. <el-input v-model="inullyApplyForm.nickname" disabled />
  200. </el-form-item>
  201. </el-col>
  202. <el-col :span="12">
  203. <el-form-item label="申请时间:">
  204. <el-input v-model="inullyApplyForm.nullyApplyTime" disabled />
  205. </el-form-item>
  206. </el-col>
  207. </el-row>
  208. <el-row>
  209. <el-col :span="24">
  210. <el-form-item label="申请原因:">
  211. <el-input
  212. type="textarea"
  213. rows="5"
  214. style="width: 100%"
  215. v-model="inullyApplyForm.nullyReason"
  216. />
  217. </el-form-item>
  218. </el-col>
  219. </el-row>
  220. </el-form>
  221. <template #footer>
  222. <div class="dialog-footer">
  223. <el-button @click="nullyApplyVisible = false">关闭</el-button>
  224. <el-button type="primary" @click="nullyApplySubmit">提交</el-button>
  225. </div>
  226. </template>
  227. </el-dialog>
  228. <!-- 退回表单Dialog -->
  229. <el-dialog v-model="callbackVisible" title="退回" width="40%">
  230. <el-form v-model="callbackForm" label-width="100px">
  231. <div class="callback_header">
  232. <span class="title">{{ callbackTitle }}</span>
  233. <ul class="callback_process">
  234. <li
  235. v-for="(item, index) in callbacks"
  236. :key="index"
  237. :class="{ active: backActivityIndex === index }"
  238. :icon="Edit"
  239. @click="selectCActivityHandle(item, index)"
  240. >
  241. <el-icon style="margin-right: 2px">
  242. <DArrowRight />
  243. </el-icon>
  244. <span>{{ item['name'] }}</span>
  245. </li>
  246. </ul>
  247. </div>
  248. <div class="callback_header">
  249. <span class="title">请填写退回原因</span>
  250. <el-input
  251. type="textarea"
  252. rows="5"
  253. style="width: 100%"
  254. v-model="callbackForm.callbackRemark"
  255. />
  256. </div>
  257. </el-form>
  258. <template #footer>
  259. <div class="dialog-footer">
  260. <el-button @click="callbackVisible = false">关闭</el-button>
  261. <el-button type="primary" @click="callbackSumbitHandle">提交</el-button>
  262. </div>
  263. </template>
  264. </el-dialog>
  265. </div>
  266. </template>
  267. <script setup lang="ts">
  268. import { Edit } from '@element-plus/icons-vue'
  269. import { defaultProps, handleTree } from '@/utils/tree'
  270. import {
  271. getHandlerCaseCenterList,
  272. getHandlerCaseCenterCount,
  273. getFlowTemplateTreeDataByUser,
  274. getFlowTemplateTree,
  275. recoverActivity,
  276. officeCenterModule,
  277. addIHangUp
  278. } from '@/api/oa/workflow'
  279. import subscribe from '@/utils/Subscribe'
  280. import { TabColumns } from './common'
  281. import { InullyApplyEnum, InullyApplyEnumType } from './inullyApply'
  282. import useLookAndApplyFlow from './lookAndApplyFlow'
  283. import useInullyApply from './inullyApply'
  284. import useCallback from './callback'
  285. defineOptions({
  286. name: 'MainOfficeCenter'
  287. })
  288. const message = useMessage()
  289. const flowTemIds = ref<any[]>([])
  290. const currentTab = ref<string>('1')
  291. const sendData = reactive({
  292. _search: false,
  293. status: '1',
  294. flowTemIds: [],
  295. searchVal: '',
  296. isMobile: false,
  297. queryMethod: 0,
  298. toSystemId: '',
  299. excludedSystemId: '',
  300. page: 1,
  301. rows: 18
  302. })
  303. const cellClassNameHandle = (item) => {
  304. if (sendData['status'] !== '1') return 'rowClass'
  305. if (!item.row['RECEIVE_TIME']) {
  306. return 'fontWeight'
  307. }
  308. return 'rowClass'
  309. }
  310. const keyValMap = {
  311. '1': 'NORMAL',
  312. '90': 'FINISH',
  313. '99': 'ARCHIVE',
  314. '40': 'CALLBACK',
  315. '20': 'HANG_UP',
  316. '160': 'OBSOLETE',
  317. '210': 'CC'
  318. }
  319. type TabType = {
  320. name: string
  321. key: string
  322. value: number
  323. column?: any[]
  324. data?: any[]
  325. }
  326. const tabList = ref<TabType[]>([])
  327. type TabTableType = {
  328. column: any[]
  329. data: any[]
  330. page: number
  331. records: number
  332. rows: number
  333. }
  334. const tabTableMapping = ref<{
  335. [key: string]: TabTableType
  336. }>(TabColumns)
  337. /**
  338. * tab初始化和角标汇总统计
  339. */
  340. const queryOfficeCenterModule = async () => {
  341. const result = await officeCenterModule()
  342. const moduleList = result['moduleList']
  343. if (moduleList && moduleList.length > 0) {
  344. tabList.value = moduleList.map((item) => {
  345. tabTableMapping.value[item['status']]['data'] = []
  346. return {
  347. name: item['name'],
  348. key: item['status'],
  349. value: 0
  350. }
  351. })
  352. tabList.value.unshift({
  353. name: '待办',
  354. key: '1',
  355. value: 0
  356. })
  357. }
  358. return tabList
  359. }
  360. const queryHandlerCaseCenterCount = async () => {
  361. getHandlerCaseCenterCount(sendData).then((result) => {
  362. tabList.value.forEach((item) => {
  363. item['value'] = result[keyValMap[item['key']]]
  364. })
  365. })
  366. }
  367. queryOfficeCenterModule().then((result) => {
  368. queryHandlerCaseCenterList()
  369. })
  370. queryHandlerCaseCenterCount()
  371. /**
  372. * 初始化流程列表
  373. */
  374. const queryHandlerCaseCenterList = () => {
  375. const key: string = sendData['status']
  376. getHandlerCaseCenterList(sendData).then((result: any) => {
  377. if (result.rows && result.rows.length > 0) {
  378. result.rows.forEach((item) => {
  379. item['btnArrJson'] = item['btnArrJson'] ? JSON.parse(item['btnArrJson']) : []
  380. })
  381. }
  382. tabTableMapping.value[key]['data'] = result.rows
  383. tabTableMapping.value[key]['records'] = result.records
  384. })
  385. }
  386. const handleCurrentChange = (pageNo: number) => {
  387. sendData['page'] = tabTableMapping.value[sendData['status']]['page'] = pageNo
  388. queryHandlerCaseCenterList()
  389. }
  390. //切换tab流程状态选项卡
  391. const switchTabHandle = (item) => {
  392. sendData['status'] = currentTab.value = item['key']
  393. sendData['page'] = tabTableMapping.value[item['key']]['page']
  394. queryFlowTemplateTreeDataByUser(item['key'])
  395. queryHandlerCaseCenterList()
  396. }
  397. const initHandleCaseCenterData = () => {
  398. queryHandlerCaseCenterCount()
  399. queryHandlerCaseCenterList()
  400. }
  401. subscribe.on('updateHandleCenterEvent', () => {
  402. //同步更新办件中心角标
  403. initHandleCaseCenterData()
  404. })
  405. /**
  406. * 初始化新建流程中流程模板Tree
  407. */
  408. const flowTemplateTree = ref<any>()
  409. const initFlowTemplateTree = () => {
  410. getFlowTemplateTree().then((result) => {
  411. const tree = result.data[0]['children']
  412. filterTree(tree, [
  413. '开票申请',
  414. '分包申请',
  415. '外包申请',
  416. '合同签订',
  417. '项目验收',
  418. '项目结项',
  419. '分包合同签订',
  420. '外包合同签订'
  421. ])
  422. flowTemplateTree.value = tree
  423. })
  424. }
  425. initFlowTemplateTree()
  426. const filterTree = (tree: any[], arr: string[]) => {
  427. if (tree.length > 0) {
  428. for (let i = 0; i < tree.length; i++) {
  429. if (arr.includes(tree[i]['name'])) {
  430. tree.splice(i, 1)
  431. --i
  432. continue
  433. }
  434. if (tree[i]['children'] && tree[i]['children'].length > 0) {
  435. filterTree(tree[i]['children'], arr)
  436. }
  437. }
  438. }
  439. }
  440. /**
  441. * 初始化搜索条件中流程名称Tree结构数据
  442. */
  443. const flowTemplates = ref<any[]>([])
  444. const queryFlowTemplateTreeDataByUser = (officeStatus = '1') => {
  445. const formData = new FormData()
  446. formData.append('isRight', '0')
  447. formData.append('toSystemId', '') //后台获取
  448. formData.append('excludedSystemId', '') //后台获取
  449. formData.append('officeStatus', officeStatus)
  450. getFlowTemplateTreeDataByUser(formData).then((result: any) => {
  451. flowTemplates.value = handleTree(result, 'id', 'pid')
  452. })
  453. }
  454. queryFlowTemplateTreeDataByUser()
  455. const searchForm = ref({
  456. name: ''
  457. })
  458. const treeRef = ref()
  459. const searchHandle = () => {
  460. //@ts-ignore
  461. sendData.flowTemIds = flowTemIds.value
  462. queryHandlerCaseCenterCount()
  463. handleCurrentChange(1)
  464. }
  465. const { addTProcessHandle, openFlowHandle, flowApplyHandle, describtionHandle } =
  466. useLookAndApplyFlow()
  467. const commandHandle = (name, item) => {
  468. if (name === '作废') {
  469. cancelFlow(item)
  470. } else if (name === '退回') {
  471. callbackActivity(item)
  472. }
  473. return
  474. }
  475. /**
  476. * 初始化作废Hook
  477. */
  478. const {
  479. nullyApplyTitle,
  480. nullyApplyVisible,
  481. inullyApplyForm,
  482. cancelFlow,
  483. nullyApplySubmit,
  484. recoverINullyApplyHandle,
  485. completelyVoidINullyApplyHandle
  486. } = useInullyApply({
  487. success: (type: InullyApplyEnumType) => {
  488. message.success(
  489. type === InullyApplyEnum.Recover
  490. ? '恢复作废成功!'
  491. : type === InullyApplyEnum.Nully
  492. ? '作废成功!'
  493. : '彻底作废成功!'
  494. )
  495. if (type === InullyApplyEnum.Recover || type === InullyApplyEnum.Nully) {
  496. subscribe.emit('updateHandleCenterCorner', null)
  497. }
  498. initHandleCaseCenterData()
  499. }
  500. })
  501. const backActivityIndex = ref<number>(0)
  502. const selectCActivityHandle = (item, index) => {
  503. backActivityIndex.value = index
  504. }
  505. const callbackSumbitHandle = () => {
  506. callbackSumbit(backActivityIndex.value)
  507. }
  508. /**
  509. * 初始化退回Hook
  510. */
  511. const {
  512. callbackVisible,
  513. callbackTitle,
  514. callbacks,
  515. callbackForm,
  516. callbackActivity,
  517. callbackSumbit
  518. } = useCallback({
  519. success: (msg: string) => {
  520. message.success(msg)
  521. initHandleCaseCenterData()
  522. }
  523. })
  524. /***
  525. * 追回
  526. */
  527. const recoverActivityHandle = (item: any) => {
  528. recoverActivity({
  529. activityInstanceId: item['ACTIVITYINSID'],
  530. participantId: item['PARTICIPANTID']
  531. }).then((res) => {
  532. if (res) {
  533. message.success('追回成功!')
  534. initHandleCaseCenterData()
  535. }
  536. })
  537. }
  538. /***
  539. * 解挂
  540. */
  541. const addIHangUpHandle = (item: any) => {
  542. addIHangUp({
  543. activityInstanceId: item['ACTIVITYINSID'],
  544. isRecover: 1
  545. }).then((res) => {
  546. if (res) {
  547. message.success('解挂成功!')
  548. }
  549. })
  550. }
  551. const keydownHandle = (evt) => {
  552. const keyCode = evt.keyCode
  553. if (keyCode === 13) {
  554. searchHandle()
  555. }
  556. }
  557. </script>
  558. <style lang="scss" scoped>
  559. @import './index.scss';
  560. </style>