index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. <template>
  2. <view class="flow-popup-content">
  3. <u-form ref="dataForm" :model="dataForm" :label-width="250" :errorType="['toast']">
  4. <view class="content">
  5. <u-form-item label="分支选择" prop="branchList" required v-if="isBranch">
  6. <JnpfSelect v-model="dataForm.branchList" @change="branchChange" placeholder="请选择审批分支"
  7. :options="branchList" multiple :props="props" />
  8. </u-form-item>
  9. <u-form-item class="back-item" label="退回节点" v-if="config.type === 'back' && config.backType"
  10. prop="backNodeCode" required>
  11. <view class="u-flex-col back-item-inner">
  12. <JnpfSelect v-model="dataForm.backNodeCode" :options="config.backNodeList" :props="props"
  13. :disabled="config.backNodeCode != 2" />
  14. <view class="u-m-t-20 selectNode u-flex" v-if="config.backType == 3">
  15. <u-radio-group v-model="dataForm.backType">
  16. <u-radio @change="radioChange(item)" v-for="(item, index) in list" :key="index"
  17. :name="item.name" :disabled="item.disabled">
  18. {{ item.fullName }}
  19. </u-radio>
  20. </u-radio-group>
  21. </view>
  22. </view>
  23. </u-form-item>
  24. <u-form-item label="转审给谁" prop="handleIds"
  25. v-if="['transfer'].includes(config.type) && propertiesType !== 'processing'" required>
  26. <JnpfUserSelect v-model=" dataForm.handleIds" />
  27. </u-form-item>
  28. <u-form-item label="转办给谁" prop="handleIds"
  29. v-if="['transfer'].includes(config.type) && propertiesType === 'processing'" required>
  30. <JnpfUserSelect v-model=" dataForm.handleIds" />
  31. </u-form-item>
  32. <u-form-item label="协办给谁" prop="handleVal" v-if="['assist'].includes(config.type)" required>
  33. <JnpfUserSelect v-model=" dataForm.handleVal" multiple @change="changeUserSelect" />
  34. </u-form-item>
  35. <view v-if="config.type === 'freeApprover'">
  36. <u-form-item label="加签人员" prop="addSignUserIdList" required>
  37. <JnpfUserSelect v-model="dataForm.addSignUserIdList" multiple />
  38. </u-form-item>
  39. <u-form-item label="加签类型">
  40. <JnpfSelect :options="typeList" v-model="dataForm.addSignType" @change="freeApproverChange" />
  41. </u-form-item>
  42. <u-form-item label="审批方式">
  43. <JnpfRadio v-model="dataForm.counterSign" :options="options" />
  44. </u-form-item>
  45. <u-form-item label="会签比例" v-if="dataForm.counterSign == 1">
  46. <view class="u-flex-col free-box">
  47. <JnpfSelect :options="ratioList" v-model="dataForm.auditRatio" />
  48. <text class="u-m-l-10 free-box-txt">达到会签比例则通过</text>
  49. </view>
  50. </u-form-item>
  51. </view>
  52. <template v-for="(item, index) in candidateList" :key="index" v-if="showCandidate">
  53. <u-form-item :label="item.nodeName+ '审批人'" :required="!item.selected" :border-bottom="false">
  54. <u-input type="select" v-model="item.fullName" placeholder="请选择审批候选人" input-align="right"
  55. @click="openSelect(item)" v-if="item.hasCandidates" />
  56. <JnpfUserSelect v-model="item.value" multiple placeholder="请选择审批候选人" v-else />
  57. </u-form-item>
  58. <u-form-item label="已选审批人" v-if="item.selected">
  59. <u-input type="textarea" v-model="item.selected" class="textarea" border disabled
  60. placeholder="" />
  61. </u-form-item>
  62. </template>
  63. <u-form-item v-if="showOpinion" :label="opinionTitle">
  64. <HandleOpinion :commonList="commonList" v-model="dataForm.handleOpinion"
  65. @addCommonWords="addCommonWords" :showCommon="false"></HandleOpinion>
  66. </u-form-item>
  67. <u-form-item prop="handleOpinion" required label-position="top" :label="opinionTitle"
  68. v-if="showApproval">
  69. <HandleOpinion :commonList="commonList" v-model="dataForm.handleOpinion"
  70. @addCommonWords="addCommonWords"></HandleOpinion>
  71. </u-form-item>
  72. <template v-if="config.approvalField.length">
  73. <u-form-item label-position="left" :label="item.fieldName"
  74. v-for="(item,index) in config.approvalField" :key="index">
  75. <JnpfInput v-if="item.jnpfKey=='input'" v-model="item.value" />
  76. <JnpfTextarea v-if="item.jnpfKey=='textarea'" v-model="item.value" />
  77. <JnpfInputNumber v-if="item.jnpfKey=='inputNumber'" v-model="item.value" />
  78. </u-form-item>
  79. </template>
  80. <u-form-item :prop="signRule ? 'signImg' : '' " :required="signRule" v-if="config.hasSign">
  81. <JnpfSign v-model="dataForm.signImg" signType="ApprovalSign" />
  82. </u-form-item>
  83. <!-- #ifndef APP-HARMONY -->
  84. <u-form-item v-if="config.hasFile">
  85. <view class="uploadFile">
  86. <JnpfUploadFile v-model="dataForm.fileList" :limit="3" align="left" />
  87. </view>
  88. </u-form-item>
  89. <!-- #endif -->
  90. <u-form-item label="抄送人员" v-if="showCustomCopy">
  91. <JnpfUserSelect v-model="copyIds" multiple />
  92. </u-form-item>
  93. </view>
  94. </u-form>
  95. <view class="flowBefore-actions">
  96. <CustomButton class="u-flex buttom-btn-left-inner" :btnText="$t('common.cancelText')"
  97. btnIcon="icon-ym icon-ym-add-cancel" customIcon />
  98. <u-button class="buttom-btn" type="primary" @click="confirm('confirm')">{{$t('common.okText')}}
  99. </u-button>
  100. </view>
  101. </view>
  102. </template>
  103. <script>
  104. import CustomButton from '@/components/CustomButton'
  105. import HandleOpinion from './components/HandleOpinion.vue'
  106. import {
  107. getSelector,
  108. Create
  109. } from '@/api/commonWords'
  110. export default {
  111. components: {
  112. HandleOpinion,
  113. CustomButton
  114. },
  115. data() {
  116. return {
  117. copyIds: [],
  118. selectList: [],
  119. candidateList: [],
  120. commonList: [],
  121. title: '',
  122. label: '',
  123. name: {
  124. 'reject': '拒绝',
  125. 'launchRecall': '撤回',
  126. 'auditRecall': '撤回',
  127. 'audit': '同意',
  128. 'back': '退回',
  129. 'freeApprover': '加签',
  130. 'transfer': '转审'
  131. },
  132. ratioList: [{
  133. fullName: '10%',
  134. id: 10
  135. }, {
  136. fullName: '20%',
  137. id: 20
  138. }, {
  139. fullName: '30%',
  140. id: 30
  141. }, {
  142. fullName: '40%',
  143. id: 40
  144. }, {
  145. fullName: '50%',
  146. id: 50
  147. }, {
  148. fullName: '60%',
  149. id: 60
  150. }, {
  151. fullName: '70%',
  152. id: 70
  153. }, {
  154. fullName: '80%',
  155. id: 80
  156. }, {
  157. fullName: '90%',
  158. id: 90
  159. }, {
  160. fullName: '100%',
  161. id: 100
  162. }],
  163. typeList: [{
  164. fullName: '审批前',
  165. id: 1
  166. }, {
  167. fullName: '审批后',
  168. id: 2
  169. }],
  170. options: [{
  171. fullName: '或签',
  172. id: 0
  173. }, {
  174. fullName: '会签',
  175. id: 1
  176. },
  177. {
  178. fullName: "依次审批",
  179. id: 2,
  180. }
  181. ],
  182. list: [{
  183. fullName: "重新审批",
  184. disabled: false,
  185. name: 1,
  186. },
  187. {
  188. fullName: "直接提交给我",
  189. disabled: false,
  190. name: 2,
  191. },
  192. ],
  193. props: {
  194. label: 'nodeName',
  195. value: 'nodeCode'
  196. },
  197. showCommonWords: false,
  198. dataForm: {
  199. auditRatio: 100,
  200. counterSign: 0,
  201. addSignType: 1,
  202. handleIds: '',
  203. handleVal: [],
  204. addSignUserIdList: '',
  205. fileList: [],
  206. handleOpinion: "",
  207. signImg: "",
  208. copyIds: "",
  209. branchList: [],
  210. candidateList: {},
  211. backNodeCode: "",
  212. backType: 1
  213. },
  214. config: {},
  215. show: false,
  216. selectVal: {},
  217. isCandidates: false,
  218. rules: {
  219. branchList: [{
  220. required: true,
  221. message: '请选择分支',
  222. type: 'array',
  223. trigger: 'blur,change'
  224. }],
  225. backNodeCode: [{
  226. required: true,
  227. message: '请选择退回节点',
  228. trigger: 'blur,change'
  229. }],
  230. signImg: [{
  231. required: true,
  232. message: '请签名',
  233. trigger: 'blur,change'
  234. }],
  235. addSignUserIdList: [{
  236. required: true,
  237. message: '请选择加签人员',
  238. type: 'array',
  239. trigger: 'blur,change'
  240. }],
  241. handleIds: [{
  242. required: true,
  243. message: '请选择人员',
  244. trigger: 'blur,change'
  245. }],
  246. handleVal: [{
  247. required: true,
  248. message: '请选择协办人员',
  249. trigger: 'blur,change',
  250. type: 'array'
  251. }],
  252. handleOpinion: [{
  253. required: true,
  254. message: '请输入意见',
  255. trigger: 'blur,change'
  256. }]
  257. },
  258. isCandidate: false,
  259. propertiesType: ''
  260. };
  261. },
  262. computed: {
  263. isBranch() {
  264. let show = false
  265. if (this.candidateType !== 3) show = true;
  266. this.branchList = this.config.branchList || [];
  267. if (!this.branchList.length) show = false;
  268. if (this.config.type === 'freeApprover' && this.dataForm.addSignType === 1) show = false;
  269. return show
  270. },
  271. showApproval() {
  272. return ['audit', 'reject', 'approvalButton'].includes(this.config.type)
  273. },
  274. showCustomCopy() {
  275. return this.config.isCustomCopy && ['audit', 'reject'].includes(this.config.type)
  276. },
  277. showCandidate() {
  278. return !['transfer', 'revoke', 'recall', 'back', 'assist'].includes(this.config.type) && this.isCandidate
  279. },
  280. showOpinion() {
  281. const list = ['transfer', 'assist', 'revoke', 'auditRecall', 'launchRecall', 'back', 'freeApprover']
  282. return list.includes(this.config.type)
  283. },
  284. opinionTitle() {
  285. const typeMap = {
  286. 'transfer': '转审原因',
  287. 'revoke': '撤销原因',
  288. 'assist': '协办原因',
  289. 'back': '退回意见',
  290. 'freeApprover': '加签意见',
  291. 'launchRecall': '撤回原因',
  292. 'auditRecall': '撤回原因',
  293. 'audit': '审批意见',
  294. 'reject': '审批意见'
  295. };
  296. const specialTypeMap = {
  297. 'transfer': {
  298. label: '转办原因'
  299. },
  300. 'audit': {
  301. label: '办理意见'
  302. }
  303. };
  304. const setFormRules = (rules) => {
  305. this.$nextTick(() => {
  306. this.$refs.dataForm.setRules(rules);
  307. });
  308. };
  309. const type = this.config.type;
  310. let resultLabel = typeMap[type] || '';
  311. if (specialTypeMap[type] && this.propertiesType === 'processing') resultLabel = specialTypeMap[type].label;
  312. return resultLabel;
  313. },
  314. fileLabel() {
  315. const type = this.config.type;
  316. const typeMap = {
  317. 'auditRecall': '撤回附件',
  318. 'freeApprover': '加签附件',
  319. 'back': '退回附件',
  320. 'transfer': '转审附件',
  321. 'audit': '审批附件',
  322. 'reject': '审批附件'
  323. };
  324. if (type === 'transfer' && this.propertiesType === 'processing') return '转办附件';
  325. if (type === 'audit' && this.propertiesType === 'processing') return '办理附件';
  326. const result = typeMap[type] || '';
  327. return result;
  328. },
  329. signRule() {
  330. return this.config.hasSign && (['audit', 'reject'].includes(this.config.type))
  331. }
  332. },
  333. onLoad(data) {
  334. try {
  335. this.config = JSON.parse(decodeURIComponent(data.config));
  336. } catch {
  337. this.config = JSON.parse(data.config);
  338. }
  339. uni.$on("confirm", (data, nodeCode) => {
  340. this.selectConfirm(data, nodeCode);
  341. });
  342. this.init()
  343. },
  344. onReady() {
  345. this.$refs.dataForm.setRules(this.rules);
  346. },
  347. methods: {
  348. init() {
  349. this.candidateType = this.config.candidateType; /* 1==分支 2==候选人 3==直接通过*/
  350. this.candidateList = this.config.candidateList || [];
  351. this.propertiesType = this.config?.propertiesType
  352. this.getSelector()
  353. this.handleLabel()
  354. this.config.candidateList.map(o => {
  355. this.isCandidates = o.isCandidates
  356. })
  357. this.copyIds = this.config?.circulateUser || ''
  358. this.dataForm.backNodeCode = this.config?.backNodeList?.length ? this.config.backNodeList[0].nodeCode : '';
  359. if (this.candidateType != 3) this.isCandidate = true;
  360. this.branchList = this.config.branchList || [];
  361. if (this.candidateType == 1) {
  362. let list = [];
  363. this.isCandidate = false;
  364. const defaultList = this.candidateList;
  365. for (let i = 0; i < this.dataForm.branchList.length; i++) {
  366. inner: for (let j = 0; j < this.branchList.length; j++) {
  367. let o = this.branchList[j];
  368. if (this.dataForm.branchList[i] === o.nodeCode && o.isCandidates) {
  369. this.isCandidate = true;
  370. list.push({
  371. ...o,
  372. label: o.nodeName + "审批人",
  373. });
  374. break inner;
  375. }
  376. }
  377. this.candidateList = [...defaultList, ...list];
  378. }
  379. }
  380. this.userInfo = uni.getStorageSync("userInfo") || {};
  381. this.dataForm.signImg = this.userInfo.signImg;
  382. if (this.config.type === 'freeApprover' && this.dataForm.addSignType == '1') this.isCandidates = false
  383. },
  384. handleLabel() {
  385. const config = this.config;
  386. const {
  387. type,
  388. propertiesType
  389. } = config;
  390. const typeMapping = {
  391. 'transfer': {
  392. title: propertiesType === 'processing' ? '转办' : '转审',
  393. label: '转审'
  394. },
  395. 'assist': {
  396. title: '协办',
  397. label: '协办'
  398. },
  399. 'revoke': {
  400. title: '撤销流程',
  401. label: '撤销'
  402. },
  403. 'launchRecall': {
  404. title: '撤回流程',
  405. label: '撤回'
  406. },
  407. 'reject': {
  408. title: '审批拒绝',
  409. label: '拒绝'
  410. },
  411. 'audit': (propertiesType) => ({
  412. title: propertiesType === 'processing' ? '办理' : '审批',
  413. label: '审批'
  414. }),
  415. 'auditRecall': {
  416. title: '撤回审核',
  417. label: '撤回'
  418. },
  419. 'freeApprover': {
  420. title: '加签',
  421. label: '加签'
  422. },
  423. 'back': {
  424. title: '退回',
  425. label: '退回'
  426. },
  427. 'submit': {
  428. title: '提交审核',
  429. label: '提交审核'
  430. }
  431. };
  432. const getTitleAndLabel = (typeKey, propertiesType) => {
  433. const mapping = typeMapping[typeKey];
  434. if (typeof mapping === 'function') return mapping(propertiesType);
  435. return mapping || {
  436. title: '',
  437. label: ''
  438. };
  439. };
  440. const {
  441. title,
  442. label
  443. } = getTitleAndLabel(type, propertiesType);
  444. this.title = title;
  445. this.label = label;
  446. uni.setNavigationBarTitle({
  447. title: this.title
  448. });
  449. },
  450. changeUserSelect(e) {
  451. this.dataForm.handleIds = e.join()
  452. },
  453. branchChange(e, list) {
  454. this.dataForm.branchList = e;
  455. this.candidateList = []
  456. this.init();
  457. },
  458. openSelect(item) {
  459. item.formData = this.config.formData;
  460. item.taskId = this.config.operatorId;
  461. item.selectList = item.selectList || [];
  462. item.candidateList = JSON.stringify(this.candidateList);
  463. item.delegateUser = this.config.delegateUser
  464. uni.navigateTo({
  465. url: "/pages/workFlow/candiDateUserSelect/index?data=" +
  466. encodeURIComponent(JSON.stringify(item)),
  467. });
  468. },
  469. selectConfirm(data, nodeCode) {
  470. let users = [];
  471. const item = this.candidateList.filter(o => o.nodeCode == nodeCode)[0] || {}
  472. item.value = data.map(o => o.id) || []
  473. item.fullName = (data.map(o => o.fullName) || []).join(',') || ''
  474. item.selectList = data || []
  475. for (let i = 0; i < this.candidateList.length; i++) {
  476. for (let j = 0; j < data.length; j++) {
  477. if (data[j].nodeCode === this.candidateList[i].nodeCode) {
  478. users.push(data[j].id);
  479. }
  480. }
  481. }
  482. this.$set(this.dataForm.candidateList, nodeCode, users);
  483. },
  484. getSelector() {
  485. getSelector().then(res => {
  486. this.commonList = res.data.list || []
  487. })
  488. },
  489. confirmCommonWord(e) {
  490. this.dataForm.handleOpinion = e.commonWordsText
  491. },
  492. handlePress(e) {
  493. this.$emit('handlePress')
  494. },
  495. addCommonWords() {
  496. let data = {
  497. commonWordsText: this.dataForm.handleOpinion,
  498. commonWordsType: 1
  499. }
  500. Create(data).then(res => {
  501. this.$u.toast(res.msg);
  502. })
  503. },
  504. freeApproverChange(e) {
  505. this.isCandidates = false;
  506. if (this.config.hasFreeApprover && e == 2 && this.candidateList.length) this.isCandidates = true;
  507. },
  508. confirm() {
  509. if (this.config.type === 'freeApprover') {
  510. this.dataForm.addSignParameter = {
  511. 'addSignUserIdList': this.dataForm.addSignUserIdList,
  512. 'auditRatio': this.dataForm.auditRatio,
  513. 'counterSign': this.dataForm.counterSign,
  514. 'addSignType': this.dataForm.addSignType
  515. }
  516. }
  517. if (!this.config.hasSign) delete this.dataForm.signImg
  518. this.dataForm.copyIds = Array.isArray(this.copyIds) && this.copyIds.length && this.copyIds.join()
  519. if (this.config.backType !== 3) this.dataForm.backType = this.config.backType
  520. let data = {
  521. ...this.dataForm,
  522. eventType: ['auditRecall', 'launchRecall'].includes(this.config.type) ? 'recall' : this.config
  523. .type,
  524. approvalField: this.config.approvalField
  525. }
  526. if (this.isCandidates || this.isCandidate) {
  527. let candidateList = {};
  528. for (let i = 0; i < this.candidateList.length; i++) {
  529. let item = this.candidateList[i]
  530. if (!item.selected && !item.value?.length) return this.$u.toast('候选人不能为空')
  531. candidateList[item.nodeCode] = item.value || [];
  532. }
  533. data.candidateList = candidateList;
  534. }
  535. this.$refs.dataForm.validate(valid => {
  536. if (valid) {
  537. uni.$emit('operate', data)
  538. setTimeout(() => {
  539. uni.navigateBack()
  540. }, 500)
  541. }
  542. });
  543. }
  544. }
  545. }
  546. </script>
  547. <style lang="scss">
  548. page {
  549. height: 100%;
  550. background-color: #fff !important;
  551. }
  552. ::v-deep .u-form-item--left {
  553. align-items: flex-start !important;
  554. }
  555. ::v-deep .u-form-item {
  556. line-height: 1.5rem !important;
  557. }
  558. .textarea {
  559. background-color: #f5f5f5;
  560. }
  561. ::v-deep .u-input--border {
  562. border: 1rpx solid #f5f5f5 !important;
  563. }
  564. .buttom-btn-left-inner {
  565. width: 50% !important;
  566. }
  567. .free-box {
  568. width: 100%;
  569. .free-box-txt {
  570. text-align: end;
  571. }
  572. }
  573. .flow-popup-content {
  574. padding-bottom: 88rpx;
  575. ::v-deep .u-form {
  576. .content {
  577. .u-form-item {
  578. .u-form-item__body {
  579. .u-form-item--left {
  580. // align-items: center !important;
  581. }
  582. }
  583. }
  584. }
  585. }
  586. .signature-box {
  587. border-top: none;
  588. }
  589. .content {
  590. padding: 0 20rpx;
  591. .back-item {
  592. .back-item-inner {
  593. justify-content: end;
  594. width: 100%;
  595. .selectNode {
  596. width: 100%;
  597. justify-content: flex-end;
  598. }
  599. }
  600. }
  601. .head-title {
  602. height: 80rpx;
  603. justify-content: space-between;
  604. color: #333333;
  605. }
  606. .uploadFile {
  607. width: 100%;
  608. padding-bottom: 8rpx;
  609. border-top: 1rpx solid #fbfbfc;
  610. }
  611. }
  612. }
  613. </style>