processSubmitDialog.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. <template>
  2. <ele-modal
  3. custom-class="ele-dialog-form long-dialog-form"
  4. :centered="true"
  5. :visible="processSubmitDialogFlag"
  6. :title="title"
  7. append-to-body
  8. :close-on-click-modal="false"
  9. :width="isRight ? '800px' : '450px'"
  10. :before-close="cancel"
  11. >
  12. <div style="display: flex; height: 100%; justify-content: space-between">
  13. <div class="form-box">
  14. <!-- <div style="text-align: center; padding: 5px">-->
  15. <!-- {{ form.name }}-->
  16. <!-- </div>-->
  17. <fm-generate-form
  18. :preview="true"
  19. class="el-form-box"
  20. v-if="Object.keys(form?.formJson || {}).length !== 0"
  21. :data="jsonData"
  22. :value="form.valueJson"
  23. ref="generateForm"
  24. >
  25. </fm-generate-form>
  26. <!-- <el-tree-->
  27. <!-- @check-change="handleTreeChange"-->
  28. <!-- show-checkbox-->
  29. <!-- node-key="id"-->
  30. <!-- class="filter-tree"-->
  31. <!-- check-strictly-->
  32. <!-- :data="treeData"-->
  33. <!-- :props="defaultProps"-->
  34. <!-- :filter-node-method="filterNode"-->
  35. <!-- ref="tree"-->
  36. <!-- >-->
  37. <!-- </el-tree>-->
  38. </div>
  39. <div
  40. style="
  41. align-self: center;
  42. display: flex;
  43. color: #1890ff;
  44. cursor: pointer;
  45. width: 15px;
  46. "
  47. @click="() => (isRight = !isRight)"
  48. >
  49. <span
  50. style="
  51. align-self: center;
  52. transform: scale(1.5);
  53. "
  54. :class="isRight ? 'el-icon-caret-left' : 'el-icon-caret-right'"
  55. >
  56. </span>
  57. <span style="writing-mode: vertical-rl">选择流程</span>
  58. </div>
  59. <div style="flex: 1" v-if="isRight">
  60. <el-form
  61. ref="form"
  62. :rules="rules"
  63. class="el-form-box"
  64. :model="form"
  65. label-width="80px"
  66. >
  67. <el-row>
  68. <el-col :span="12">
  69. <el-form-item label="流程分类">
  70. <ele-tree-select
  71. @change="changeLCFL"
  72. clearable
  73. ref="processTypeRef"
  74. filterable
  75. :data="LCFLList"
  76. v-model="form.LCFL"
  77. childrenKey="children"
  78. valueKey="id"
  79. labelKey="name"
  80. placeholder="请选择"
  81. default-expand-all
  82. />
  83. </el-form-item>
  84. </el-col>
  85. <el-col :span="12">
  86. <el-form-item label="发起流程">
  87. <el-select
  88. filterable
  89. v-model="form.FQLC"
  90. @change="changeFQLC"
  91. style="width: 100%"
  92. >
  93. <el-option
  94. v-for="item in processList"
  95. :key="item.id"
  96. :value="item.id"
  97. :label="item.name"
  98. ></el-option>
  99. </el-select>
  100. </el-form-item>
  101. </el-col>
  102. </el-row>
  103. <el-row>
  104. <el-col :span="12">
  105. <el-form-item label="流程名称">
  106. <el-input v-model="form.name" clearable></el-input>
  107. </el-form-item>
  108. </el-col>
  109. <el-col :span="12">
  110. <el-form-item label="流程标识">
  111. <el-input v-model="form.key" disabled></el-input>
  112. </el-form-item>
  113. </el-col>
  114. </el-row>
  115. <headerTitle
  116. title="流程执行人/流程图"
  117. style="margin-top: 30px"
  118. ></headerTitle>
  119. <el-table
  120. :data="datasource"
  121. height="150px"
  122. border
  123. style="margin-bottom: 10px"
  124. >
  125. <el-table-column
  126. label="任务节点"
  127. align="center"
  128. prop="taskDefinitionName"
  129. width="140"
  130. fixed
  131. />
  132. <el-table-column
  133. label="执行人"
  134. align="center"
  135. prop="options"
  136. min-width="140px"
  137. >
  138. <template v-slot="scope">
  139. <div
  140. v-if="scope.row.type !== 60 && scope.row.options.length > 0"
  141. >
  142. <el-tag
  143. size="medium"
  144. :key="option"
  145. v-for="option in scope.row.options"
  146. >
  147. {{ getAssignRuleOptionName(scope.row, option) }}
  148. </el-tag>
  149. </div>
  150. <el-tag size="medium" v-if="scope.row.type === 60">
  151. {{ getAssignRuleOptionName(scope.row) }}
  152. </el-tag>
  153. </template>
  154. </el-table-column>
  155. </el-table>
  156. <my-process-viewer
  157. style="min-width: 100%; height: 200px"
  158. key="designer"
  159. v-model="bpmnXML"
  160. />
  161. </el-form>
  162. </div>
  163. </div>
  164. <div slot="footer">
  165. <el-form :model="form">
  166. <el-form-item label="通知人:">
  167. <el-button
  168. style="float: left; margin-top: 5px"
  169. icon="el-icon-circle-plus-outline"
  170. round
  171. @click="handleEditDataScope"
  172. ></el-button>
  173. <div style="display: flex; flex-direction: row; flex-wrap: wrap">
  174. <el-tag
  175. style="float: left; margin: 5px 5px 0 5px"
  176. v-for="(item, index) in form.noticeScope"
  177. :key="item.name"
  178. effect="plain"
  179. closable
  180. :title="item.name"
  181. @close="handleCloseTag(index)"
  182. >
  183. {{
  184. item.name.length > 3
  185. ? item.name[0] + item.name[1] + item.name[2] + '...'
  186. : item.name
  187. }}
  188. </el-tag>
  189. </div>
  190. <!-- <ele-tree-select-->
  191. <!-- @change="handleTreeChange"-->
  192. <!-- filterable-->
  193. <!-- style="width: 100%"-->
  194. <!-- ref="treeSelect"-->
  195. <!-- multiple-->
  196. <!-- clearable-->
  197. <!-- :data="treeData"-->
  198. <!-- v-model="treeList"-->
  199. <!-- placeholder="请选择"-->
  200. <!-- valueKey="id"-->
  201. <!-- labelKey="name"-->
  202. <!-- :checkStrictly="true"-->
  203. <!-- />-->
  204. </el-form-item>
  205. </el-form>
  206. <!-- <el-button-->
  207. <!-- type="primary"-->
  208. <!-- size="small"-->
  209. <!-- v-if="active == 0"-->
  210. <!-- @click="() => ((active = 1), (isRight = true))"-->
  211. <!-- >下一步-->
  212. <!-- </el-button>-->
  213. <!-- <el-button-->
  214. <!-- type="primary"-->
  215. <!-- size="small"-->
  216. <!-- v-if="active == 1"-->
  217. <!-- @click="() => (active = 0)"-->
  218. <!-- >上一步-->
  219. <!-- </el-button>-->
  220. <el-button type="primary" size="small" @click="submit">提交</el-button>
  221. <el-button size="small" @click="cancel">关闭</el-button>
  222. <!-- <visibility-range-dialog-->
  223. <!-- :visibilityRangeDialogFlag.sync="visibilityRangeDialogFlag"-->
  224. <!-- v-if="visibilityRangeDialogFlag"-->
  225. <!-- @getTreeList="getTreeList"-->
  226. <!-- ref="visibilityRangeDialogRef"-->
  227. <!-- ></visibility-range-dialog>-->
  228. <staffSelection
  229. ref="staffSelection"
  230. @confirm="getTreeList"
  231. ></staffSelection>
  232. </div>
  233. </ele-modal>
  234. </template>
  235. <script>
  236. import {
  237. getModelPage,
  238. getProcessDefinitionBpmnXML,
  239. getProcessDefinitionInfo,
  240. getTaskAssignRuleList,
  241. listAllUserBind,
  242. listSimpleUserGroups,
  243. processInstanceCreateAPI
  244. } from './api';
  245. import { treeClassifyCodeEnum } from '@/enum/dict';
  246. import { listRoles } from '@/api/system/role';
  247. import { listOrganizations } from '@/api/system/organization';
  248. import dictMixins from '@/mixins/dictMixins';
  249. import { getByCode } from '@/api/system/dictionary-data';
  250. import { getGroupUserTree, getProduceTreeByCode } from '@/api/main';
  251. import visibilityRangeDialog from './visibilityRangeDialog.vue';
  252. import { getModel } from '@/api/bpm/model';
  253. import { getProcessDefinitionList } from '@/api/bpm/definition';
  254. import { getToken } from '@/utils/token-util';
  255. import { mapGetters } from 'vuex';
  256. import staffSelection from '@/components/staffSelection/staffSelection.vue';
  257. export default {
  258. name: 'processSubmitDialog',
  259. components: { visibilityRangeDialog, staffSelection },
  260. mixins: [dictMixins],
  261. props: {
  262. processSubmitDialogFlag: {
  263. type: Boolean,
  264. default: false
  265. }
  266. },
  267. watch: {
  268. filterText(val) {
  269. this.$refs.tree.filter(val);
  270. }
  271. },
  272. data() {
  273. return {
  274. visibilityRangeDialogFlag: false,
  275. isRight: false,
  276. jsonData: {},
  277. form: {
  278. LCFL: '',
  279. FQLC: '',
  280. processDefinitionId: '',
  281. name: '',
  282. businessId: '',
  283. noticeScope: [],
  284. formJson: {},
  285. valueJson: {},
  286. businessKey: ''
  287. },
  288. title: '',
  289. active: 0,
  290. bpmnXML: null,
  291. filterText: null,
  292. treeList: [],
  293. treeData: [],
  294. defaultProps: {
  295. children: 'children',
  296. label: 'name'
  297. },
  298. LCFLList: [],
  299. processList: [],
  300. datasource: [],
  301. roleOptions: [],
  302. deptOptions: [],
  303. deptTreeOptions: [],
  304. postOptions: [],
  305. userOptions: [],
  306. userGroupOptions: [],
  307. dictList: {},
  308. rules: {}
  309. };
  310. },
  311. computed: {
  312. ...mapGetters(['user'])
  313. },
  314. async created() {
  315. let typeObj = await getProduceTreeByCode(
  316. treeClassifyCodeEnum['PROCESSTYPE']
  317. );
  318. // await this.getTreeData();
  319. this.LCFLList = typeObj[0].children;
  320. // 获得角色列表
  321. this.roleOptions = [];
  322. listRoles({
  323. current: 1,
  324. size: 9999
  325. }).then((data) => {
  326. this.roleOptions.push(...data.list);
  327. });
  328. // 获得部门列表
  329. this.deptOptions = [];
  330. this.deptTreeOptions = [];
  331. listOrganizations().then((data) => {
  332. this.deptOptions.push(...data);
  333. this.deptTreeOptions.push(...this.handleTree(data, 'id'));
  334. });
  335. // 获得岗位列表 暂无岗位概念
  336. this.postOptions = [];
  337. /*listSimplePosts().then(response => {
  338. this.postOptions.push(...response.data);
  339. });*/
  340. // 获得用户列表
  341. this.userOptions = [];
  342. listAllUserBind().then((data) => {
  343. this.userOptions.push(...data);
  344. });
  345. // 获得用户组列表
  346. this.userGroupOptions = [];
  347. listSimpleUserGroups().then((response) => {
  348. this.userGroupOptions.push(...response);
  349. });
  350. //this.dictEnum['工作流任务分配自定义脚本']
  351. await this.getDictList(this.dictEnum['工作流任务分配自定义脚本']);
  352. await this.getDictList(this.dictEnum['工种类型']);
  353. },
  354. mounted() {
  355. //this.isRight = false
  356. },
  357. methods: {
  358. async init(row = {}) {
  359. this.title = row.name; //'新建' + row.name + '单';
  360. this.form = _.cloneDeep(row);
  361. this.form.formId = row.id;
  362. this.$set(this.form, 'noticeScope', []);
  363. this.$set(this.form, 'LCFL', '');
  364. this.$set(this.form, 'FQLC', '');
  365. this.$set(this.form, 'name', '');
  366. this.$set(this.form, 'key', '');
  367. this.$set(this.form, 'valueJson', {});
  368. this.jsonData = JSON.parse(this.form.formJson.makingJson);
  369. this.jsonData.config.dataSource &&
  370. this.jsonData.config.dataSource.forEach((item) => {
  371. item.headers = {
  372. Authorization: getToken()
  373. };
  374. // item.url = item.url && item.url.replace('/api', this.APIUrl);
  375. });
  376. this.jsonData.list.forEach((item) => {
  377. if (item.type == 'deptAndUserCascader') {
  378. }
  379. if (item.type == 'deptCascader') {
  380. if (item.options.isDefaultLoginUser) {
  381. this.form.valueJson[item.model] = this.user.info.groupId;
  382. }
  383. }
  384. if (item.type == 'userSelect') {
  385. if (item.options.isDefaultLoginUser) {
  386. this.form.valueJson[item.model] = this.user.info.userId;
  387. }
  388. }
  389. if (item.type == 'imgupload') {
  390. // this.form.valueJson[item.model].length &&
  391. // this.form.valueJson[item.model].forEach((item) => {
  392. // //item.objectUrl = this.APIUrl +'/kd-aiot' + item.storePath
  393. // // item.objectUrl = item.url;
  394. // // console.log(item);
  395. // });
  396. }
  397. });
  398. await this.getProcessDefinitionBpmnXMLInfo(this.form.processModelId);
  399. await this.getTaskAssignRuleListInfo({
  400. modelId: this.form.processModelId,
  401. processDefinitionId: this.form.processDefinitionId
  402. });
  403. await this.getDefaultInfo(row.processModelId);
  404. },
  405. async getDefaultInfo(businessKey) {
  406. let info = await getModel(businessKey);
  407. //let info = await getProcessDefinitionInfo({ id: businessKey });
  408. this.form.LCFL = info?.category;
  409. this.form.FQLC = info?.id;
  410. this.form.name = this.title;
  411. this.form.key = info?.key;
  412. if (this.form.LCFL) await this.getProcessList(this.form.LCFL);
  413. },
  414. async getProcessList(val) {
  415. let params = {
  416. pageNo: 1,
  417. pageSize: 999,
  418. processTypeId: val
  419. };
  420. const { list } = await getModelPage(params);
  421. this.processList = list.filter((item) => item.processDefinition);
  422. },
  423. async getProcessDefinitionBpmnXML(val) {
  424. this.bpmnXML = await getProcessDefinitionBpmnXML(val);
  425. },
  426. async getProcessDefinitionBpmnXMLInfo(val) {
  427. // 加载流程图
  428. let res = await getModel(val);
  429. this.bpmnXML = res.bpmnXml;
  430. },
  431. async getTaskAssignRuleListInfo(find) {
  432. this.datasource = await getTaskAssignRuleList({
  433. modelId: find.modelId,
  434. processDefinitionId: find.processDefinitionId
  435. });
  436. },
  437. handleEditDataScope() {
  438. this.$refs.staffSelection.open(this.form.noticeScope);
  439. // this.visibilityRangeDialogFlag = true;
  440. // this.$nextTick(() => {
  441. // this.$refs.visibilityRangeDialogRef.init(this.form);
  442. // });
  443. },
  444. getTreeList(list) {
  445. list.forEach((item) => {
  446. item.type = 1;
  447. });
  448. this.form.noticeScope = list;
  449. },
  450. handleCloseTag(index) {
  451. this.form.noticeScope.splice(index, 1);
  452. },
  453. getAssignRuleOptionName(row, option) {
  454. if (row.type == 10) {
  455. for (const roleOption of this.roleOptions) {
  456. if (roleOption.id === option) {
  457. return roleOption.name;
  458. }
  459. }
  460. } else if (row.type === 20 || row.type === 21) {
  461. for (const deptOption of this.deptOptions) {
  462. if (deptOption.id === option) {
  463. return deptOption.name;
  464. }
  465. }
  466. } else if (row.type === 22) {
  467. option = option + ''; // 转换成 string
  468. return this.getDictV(this.dictEnum['工种类型'], option);
  469. } else if (row.type === 30 || row.type === 31 || row.type === 32) {
  470. for (const userOption of this.userOptions) {
  471. if (userOption.id === option) {
  472. return userOption.nickname || userOption.name;
  473. }
  474. }
  475. } else if (row.type === 40) {
  476. for (const userGroupOption of this.userGroupOptions) {
  477. if (userGroupOption.id === option) {
  478. return userGroupOption.name;
  479. }
  480. }
  481. } else if (row.type === 50) {
  482. option = option + ''; // 转换成 string
  483. return this.getDictV(
  484. this.dictEnum['工作流任务分配自定义脚本'],
  485. option
  486. );
  487. } else if (row.type === 60) {
  488. return row.variableName;
  489. }
  490. return '未知(' + option + ')';
  491. },
  492. async getTreeData() {
  493. this.treeData = await getGroupUserTree();
  494. },
  495. filterNode(value, data) {
  496. if (!value) return true;
  497. return data.name.indexOf(value) !== -1;
  498. },
  499. /**
  500. * 构造树型结构数据
  501. * @param {*} data 数据源
  502. * @param {*} id id字段 默认 'id'
  503. * @param {*} parentId 父节点字段 默认 'parentId'
  504. * @param {*} children 孩子节点字段 默认 'children'
  505. * @param {*} rootId 根Id 默认 0
  506. */
  507. handleTree(data, id, parentId, children, rootId) {
  508. id = id || 'id';
  509. parentId = parentId || 'parentId';
  510. children = children || 'children';
  511. rootId =
  512. rootId ||
  513. Math.min.apply(
  514. Math,
  515. data.map((item) => {
  516. return item[parentId];
  517. })
  518. ) ||
  519. 0;
  520. //对源数据深度克隆
  521. const cloneData = JSON.parse(JSON.stringify(data));
  522. //循环所有项
  523. const treeData = cloneData.filter((father) => {
  524. let branchArr = cloneData.filter((child) => {
  525. //返回每一项的子级数组
  526. return father[id] == child[parentId];
  527. });
  528. branchArr.length > 0 ? (father.children = branchArr) : '';
  529. //返回第一层
  530. return father[parentId] == rootId;
  531. });
  532. return treeData !== '' ? treeData : data;
  533. },
  534. getDictV(code, val) {
  535. if (!this.dictList[code]) return '';
  536. return this.dictList[code].find((item) => item.value == val)?.label;
  537. },
  538. async getDictList(code) {
  539. let { data: res } = await getByCode(code);
  540. this.dictList[code] = res.map((item) => {
  541. let values = Object.keys(item);
  542. return {
  543. value: values[0],
  544. label: item[values[0]]
  545. };
  546. });
  547. },
  548. generateFormValid(validate = true) {
  549. return this.$refs.generateForm.getData(validate).then((data) => {
  550. return data;
  551. });
  552. },
  553. handleTreeChange() {
  554. this.$nextTick(() => {
  555. this.form.noticeScope =
  556. this.$refs.treeSelect.$refs.tree.getCheckedNodes();
  557. console.log(this.form.noticeScope);
  558. });
  559. },
  560. async changeLCFL(val) {
  561. this.bpmnXML = null;
  562. this.form.processDefinitionId = null;
  563. this.form.FQLC = null;
  564. await this.getProcessList(val);
  565. },
  566. async changeFQLC(val) {
  567. if (!val) return;
  568. let find = this.processList.find((item) => item.id === val) || {};
  569. this.form.name = find.name;
  570. this.form.key = find.key;
  571. this.form.processDefinitionId = find.processDefinition.id;
  572. this.form.processModelId = val;
  573. await this.getProcessDefinitionBpmnXML(find.processDefinition.id);
  574. await this.getTaskAssignRuleListInfo({
  575. modelId: find.id,
  576. processDefinitionId: find.processDefinition.id
  577. });
  578. },
  579. async submit() {
  580. this.form.valueJson = await this.generateFormValid();
  581. this.form.processType = '1';
  582. await processInstanceCreateAPI({
  583. ...this.form,
  584. variables: { ...this.form.valueJson }
  585. });
  586. this.$message('提交审核成功');
  587. this.$emit('reload');
  588. this.cancel();
  589. },
  590. cancel() {
  591. this.$emit('update:processSubmitDialogFlag', false);
  592. }
  593. }
  594. };
  595. </script>
  596. <style scoped lang="scss">
  597. .box {
  598. display: flex;
  599. height: 450px;
  600. .left-box {
  601. width: 55%;
  602. border-right: 1px solid #e2e4e7;
  603. padding-right: 15px;
  604. height: 100%;
  605. .el-tree {
  606. height: 85%;
  607. overflow-y: auto;
  608. }
  609. }
  610. .right-box {
  611. flex: 1;
  612. padding-left: 20px;
  613. height: 100%;
  614. .right-box-item {
  615. margin-top: 5px;
  616. display: flex;
  617. justify-content: space-between;
  618. align-items: center;
  619. }
  620. }
  621. }
  622. .form-box {
  623. max-height: 500px;
  624. min-width: 400px;
  625. overflow: auto;
  626. }
  627. ::v-deep .el-dialog {
  628. min-width: 400px;
  629. .el-dialog__header {
  630. background: #1890ffd6;
  631. }
  632. .el-dialog__title,
  633. .el-dialog__close {
  634. color: #ffffff;
  635. }
  636. }
  637. </style>