processSubmitDialog.vue 18 KB

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