index.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. <template>
  2. <page-meta root-font-size="system" />
  3. <view class="process_detail_container">
  4. <uni-card>
  5. <view class="main_container">
  6. <uni-section titleFontSize="1.3rem" title="申请内容" type="line"></uni-section>
  7. <uni-forms ref="$mainForm" :rules="$mainFormRules" :modelValue="mainFormValue" label-position="left"
  8. :label-width="125" :border="true">
  9. <view v-for="(elem, index) in formElements" :key="index">
  10. <uni-forms-item
  11. v-if="!(elem.elementName.endsWith('审批') && '' == elem.defaultValue && ['0', undefined].includes(elem.canEdit))"
  12. :label="elem.elementName" :name="elem.elementId">
  13. <view class="element_value_container">
  14. <view v-if="'1' == elem.canEdit && '8' != elem.type" class="element_value">
  15. <!-- 富文本框 -->
  16. <uni-easyinput v-if="'1' == elem.type" placeholder="请输入内容"
  17. v-model="elem.defaultValue" type="textarea"></uni-easyinput>
  18. <!-- 文本框 -->
  19. <uni-easyinput v-else-if="'0' == elem.type" placeholder="请输入内容"
  20. v-model="elem.defaultValue" type="text"></uni-easyinput>
  21. <!-- 下拉框 -->
  22. <picker class="picker_container" v-else-if="'2' == elem.type"
  23. @change="bindPickerChange($event, elem)" :value="elem.defaultValue"
  24. :range="formatDict(elem.typeDetail.enum)">
  25. <view class="uni-input input_text">
  26. <!-- 设置默认值为第一个选项 -->
  27. {{ elem.defaultValue ? elem.defaultValue : elem.defaultValue =
  28. elem.typeDetail.enum[0].enumVname }}
  29. </view>
  30. </picker>
  31. <!-- 年月日 时分秒 -->
  32. <uni-datetime-picker v-else-if="'9' == elem.type" v-model="elem.defaultValue"
  33. type="datetime" />
  34. <!-- 年月日 -->
  35. <uni-datetime-picker v-else-if="'3' == elem.type" v-model="elem.defaultValue"
  36. type="date" />
  37. <!-- 审批签字板 -->
  38. <view v-else-if="'13' == elem.type">
  39. <button v-if="elem.defaultValue == ''" type="primary"
  40. @click="handleSignature(index)">签名</button>
  41. <view v-else class="signature_img">
  42. <img style="width: 100%;" mode="widthFix" @click="handleSignature(index)"
  43. :src="config.baseUrlPre + elem.sealImgPath"
  44. :alt="elem.elementName + '签名'" />
  45. </view>
  46. </view>
  47. </view>
  48. <!-- TODO 修改后端 我的 在办 办结 接口签名图片地址返回参数为sealImgPath -->
  49. <view v-else-if="typeof elem.defaultValue === 'string' && elem.defaultValue.startsWith('/shares')" class="signature_img">
  50. <img style="width: 100%;" mode="widthFix"
  51. :src="config.baseUrlPre + elem.defaultValue" />
  52. </view>
  53. <view v-else-if="typeof elem.sealImgPath === 'string' && elem.sealImgPath.startsWith('/shares')" class="signature_img">
  54. <img style="width: 100%;" mode="widthFix"
  55. :src="config.baseUrlPre + elem.sealImgPath" />
  56. </view>
  57. <text class="element_value" v-else>{{ elem.defaultValue }}</text>
  58. </view>
  59. </uni-forms-item>
  60. </view>
  61. </uni-forms>
  62. </view>
  63. </uni-card>
  64. <view class="repeating_table">
  65. <!-- 重复表 -->
  66. <uni-card v-if="flowInfo.seModel == '0' && repeatingFormNotEmpty" spacing="0" padding="0">
  67. <button @click="handlerepeatingForm" type="primary">查看明细</button>
  68. </uni-card>
  69. <uni-card v-else v-for="(table, tableIndex) in repeatingForm.elements" spacing="0" :key="tableIndex">
  70. <uni-forms label-position="left" :label-width="125" :border="true">
  71. <uni-forms-item :name="elem.tableField" v-for="(elem, itemIndex) in table"
  72. :label="repeatingForm.elementItem[itemIndex].elementName.slice(3)" :key="itemIndex">
  73. <uni-easyinput placeholder="请输入内容" v-model="elem.defaultValue"
  74. :type="fieldTypeDict[repeatingForm.elementItem[itemIndex].fieldType.value] || 'text'"></uni-easyinput>
  75. </uni-forms-item>
  76. </uni-forms>
  77. <!-- <view class="repeating_table_button_container">
  78. <uni-row>
  79. <uni-col :span="10" :offset="1">
  80. <button @click="addrepeatingFormItem(tableIndex)" type="primary">新增</button>
  81. </uni-col>
  82. <uni-col :span="10" :offset="2">
  83. <button @click="delrepeatingFormItem(tableIndex)"
  84. :disabled="repeatingForm.elements.length <= 1" type="warn">删除</button>
  85. </uni-col>
  86. </uni-row>
  87. </view> -->
  88. </uni-card>
  89. <uni-popup v-if="flowInfo.seModel == '0' && repeatingFormNotEmpty" ref="repeatingFormPopup">
  90. <uni-card margin="0px" spacing="0px" padding="0px">
  91. <view class="repeating_table_container">
  92. <uni-table :border="true" stripe>
  93. <uni-tr>
  94. <uni-th align="center" v-for="(item, index) in repeatingForm.elementItem" :key="index">
  95. {{ item.elementName.slice(3, 5) }}
  96. </uni-th>
  97. </uni-tr>
  98. <uni-tr v-for="(table, tableIndex) in repeatingForm.elements" :key="tableIndex">
  99. <uni-td align="center" v-for="(elem, itemIndex) in table" :key="itemIndex">
  100. {{ elem.defaultValue }}
  101. </uni-td>
  102. </uni-tr>
  103. <!-- (列数 - 1) * 总行数 + 当前行数 -->
  104. <!-- <uni-tr v-for="(item, index) in (repeatingForm.elements.length / repeatingForm.elementItem.length)" :key="index">
  105. <uni-td align="center" v-for="col in repeatingForm.elementItem.length" :key="col">
  106. {{ repeatingForm.elements[(col - 1) * (repeatingForm.elements.length / repeatingForm.elementItem.length) + index]!.defaultValue }}
  107. </uni-td>
  108. </uni-tr> -->
  109. </uni-table>
  110. </view>
  111. </uni-card>
  112. </uni-popup>
  113. </view>
  114. <!-- 附件 -->
  115. <view v-for="(item, index) in fileList" :key="index">
  116. <uni-card v-if="item.files.length != undefined && item.files.length > 0">
  117. <uni-section titleFontSize="1.3rem" title="附件" type="line"></uni-section>
  118. <attachment-list @clickDelete="deleteFile" :canEdit="flowInfo.seModel == '1'" :attachments="item.files"></attachment-list>
  119. </uni-card>
  120. </view>
  121. <!-- 上传附件 -->
  122. <view v-if="processInfo.tinsId || flowInfo.seModel == '1'" class="file_picker_container">
  123. <uni-card title="上传附件" :extra="`${subFileList.length}/15`" spacing="0">
  124. <uni-file-picker ref="filePicker" v-model="subFileList" :auto-upload="true" mode="list" limit="10"
  125. file-mediatype="all" @select="handleFileSelect" @progress="handleFileProgress"
  126. @success="handleFileSuccess" @fail="handleFileFail" @delete="handleFileDelete" />
  127. </uni-card>
  128. </view>
  129. <!-- 流转过程 -->
  130. <view>
  131. <uni-card>
  132. <view class="flow_step_container">
  133. <uni-section titleFontSize="1.3rem" title="流转过程" type="line"></uni-section>
  134. <up-steps :current="stepActive" activeColor="#18bc37" inactiveColor="#2979ff" direction="column">
  135. <view v-for="(step, index) in options">
  136. <up-steps-item :contentClass="'redcontent'" v-if="step.state == 3"
  137. :title="step.title + ' 退回'" :desc="step.desc" :key="index" error></up-steps-item>
  138. <up-steps-item :contentClass="'redcontent'" v-else-if="step.state == 0"
  139. :title="step.title + ' 撤销'" :desc="step.desc" :key="index" error></up-steps-item>
  140. <up-steps-item v-else-if="index == stepActive" :title="step.title" :desc="step.desc"
  141. :key="index">
  142. <template #icon>
  143. <view class="active_step_circle">
  144. <text class="active_step_text">{{ index + 1 }}</text>
  145. </view>
  146. </template>
  147. </up-steps-item>
  148. <up-steps-item v-else :title="step.title" :desc="step.desc" :key="index"></up-steps-item>
  149. </view>
  150. </up-steps>
  151. </view>
  152. </uni-card>
  153. </view>
  154. <view v-if="processInfo.tinsId">
  155. <view class="remark_container">
  156. <uni-card>
  157. <uni-section titleFontSize="1.3rem" title="环节备注" type="line"></uni-section>
  158. <view class="remark_content">
  159. <uni-easyinput type="textarea" autoHeight v-model="remark" placeholder="请输入"></uni-easyinput>
  160. </view>
  161. </uni-card>
  162. </view>
  163. </view>
  164. <view v-if="processInfo.tinsId" class="approve_button">
  165. <uni-card spacing="0" padding="0">
  166. <button :disabled="!button_state" :loading="!button_state" type="primary"
  167. @click="handleSubmitProcess('1')">
  168. {{ flowInfo.seModel == '0' ? '通过' : '提交' }}
  169. </button>
  170. </uni-card>
  171. </view>
  172. </view>
  173. <view v-if="processInfo.tinsId && flowInfo.seModel == '0'">
  174. <view class="reject_button">
  175. <uni-card spacing="0" padding="0" :is-shadow="false" :border="false">
  176. <uni-row>
  177. <uni-col :span="11">
  178. <button :disabled="!button_state" :loading="!button_state" type="warn"
  179. @click="handleSubmitProcess('0')">退回上一级</button>
  180. </uni-col>
  181. <uni-col :span="11" :offset="2">
  182. <button :disabled="!button_state" :loading="!button_state" type="warn"
  183. @click="handleSubmitProcess('2')">退回发起人</button>
  184. </uni-col>
  185. </uni-row>
  186. </uni-card>
  187. </view>
  188. </view>
  189. <view v-else-if="isCancel">
  190. <view class="remark_container">
  191. <uni-card>
  192. <uni-section titleFontSize="1.3rem" title="撤销备注" type="line"></uni-section>
  193. <view class="remark_content">
  194. <uni-easyinput type="textarea" autoHeight v-model="remark" placeholder="请输入"></uni-easyinput>
  195. </view>
  196. </uni-card>
  197. </view>
  198. <view class="cancel_button_container">
  199. <uni-card spacing="0" padding="0">
  200. <button :disabled="!button_state" :loading="!button_state" type="warn"
  201. @click="handleCancelProcess">撤销</button>
  202. </uni-card>
  203. </view>
  204. </view>
  205. <view style="height: 5px; margin-top: 20px;"></view>
  206. <!-- 签字版弹出层 -->
  207. <uni-popup ref="signaturePopup" @maskClick="closeSignaturePopup">
  208. <view class="signature_container" :class="{ 'signature_container_landscape': isLandscape }">
  209. <view class="signature_content">
  210. <l-signature ref="signatureRef" v-if="signaturePopupShow" :landscape="isLandscape" :penSize="8"
  211. :minLineWidth="4" :maxLineWidth="12" :openSmooth="true" :preferToDataURL="true"
  212. backgroundColor="#ffffff" penColor="black"></l-signature>
  213. </view>
  214. <view class="signature_button_container">
  215. <uni-row :gutter="10">
  216. <uni-col :span="6">
  217. <button @click="onclickSignatureButton('undo')">撤销</button>
  218. </uni-col>
  219. <uni-col :span="6">
  220. <button @click="onclickSignatureButton('clear')">清空</button>
  221. </uni-col>
  222. <uni-col :span="6">
  223. <button @click="onclickSignatureButton('save')">保存</button>
  224. </uni-col>
  225. <uni-col :span="6">
  226. <button @click="onclickSignatureButton('landscape')">全屏</button>
  227. </uni-col>
  228. </uni-row>
  229. </view>
  230. </view>
  231. </uni-popup>
  232. </template>
  233. <script setup lang="ts">
  234. import { computed, onMounted, reactive, ref } from 'vue'
  235. import { onLoad } from '@dcloudio/uni-app'
  236. import { LSignatureToTempFilePathOptions, LSignatureToFileSuccess } from '@/uni_modules/lime-signature'
  237. import attachmentList from '@/components/ygoa/attachmentList.vue'
  238. import config from '@/config.js'
  239. import $tab from '@/plugins/tab.js'
  240. import $modal from '@/plugins/modal.js'
  241. import { getProcessFlowInfo, getProcessFormInfo, getProcessFormInfoInFlow, getProcessFlow, submitProcessFlow, cancelProcessFlow, uploadSignatureImg, uploadSignatureBoardImg, uploadFile } from '@/api/process.js'
  242. import { useUserStore } from '@/store/user.js'
  243. const fieldTypeDict = {
  244. '0': 'text',
  245. '1': 'number'
  246. }
  247. const userStore = useUserStore()
  248. const processInfo = reactive({
  249. insId: '',
  250. insName: '',
  251. control: 1,
  252. username: '',
  253. tinsId: undefined
  254. })
  255. onLoad(({ insId, tinsId, insName, control }) => {
  256. // 获取传入的标题参数
  257. const title = insName || '流程信息';
  258. processInfo.insId = insId
  259. processInfo.insName = insName
  260. processInfo.control = control
  261. if (tinsId) {
  262. processInfo['tinsId'] = tinsId
  263. }
  264. // 设置导航栏标题
  265. uni.setNavigationBarTitle({
  266. title: title
  267. });
  268. })
  269. onMounted(() => {
  270. initProcessInfo()
  271. initProcessFlow()
  272. })
  273. const repeatingForm = ref({
  274. elements: [],
  275. elementItem: [],
  276. })
  277. const repeatingFormNotEmpty = ref(false)
  278. function repeatingFormHasValue() {
  279. if (repeatingForm.value === undefined) return
  280. if (repeatingForm.value.elementItem.length <= 0) return
  281. repeatingForm.value.elements.forEach(({ defaultValue }) => {
  282. if (defaultValue != "") {
  283. repeatingFormNotEmpty.value = true
  284. return new Promise((resolve, reject) => {
  285. resolve(repeatingFormNotEmpty)
  286. })
  287. }
  288. })
  289. }
  290. // 新增重复表表单
  291. function addrepeatingFormItem(index) {
  292. // parseCalculation(repeatingForm.value.elementItem)
  293. const table = repeatingForm.value.elementItem.map(({ tableField, bddzText }) => {
  294. const item = {
  295. name: tableField,
  296. defaultValue: ""
  297. }
  298. if (bddzText != '') {
  299. const mulItem = bddzText.split('*')
  300. // 保存关联数据索引
  301. item['bddzText'] = mulItem.map(item => {
  302. // item['operation '].operands = mulItem.map(item => {
  303. return repeatingForm.value.elementItem.findIndex(({ elementName }) => item == elementName)
  304. })
  305. }
  306. return item
  307. })
  308. repeatingForm.value.elements.splice(index + 1, 0, table)
  309. }
  310. // 删除重复表表单
  311. function delrepeatingFormItem(index) {
  312. console.log("delrepeatingFormItem: " + index);
  313. $modal.confirm('', '确认删除该重复表数据')
  314. .then(() => {
  315. repeatingForm.value.elements.splice(index, 1)
  316. // $repeatingForms.value.splice(index, 1)
  317. }).catch(() => { })
  318. }
  319. const formElements = ref([])
  320. const formInfo = ref({
  321. formElements: [],
  322. formId: '',
  323. formInsId: ''
  324. })
  325. const fileList = ref([])
  326. const isCancel = ref(false)
  327. function initProcessInfo() {
  328. if (processInfo.tinsId) {
  329. getProcessFormInfoInFlow(userStore.user.useId, processInfo).then(({ returnParams }) => {
  330. formElements.value = returnParams.formElements
  331. formInfo.value = returnParams.formInfo[0]
  332. repeatingForm.value = returnParams.repeatingForm
  333. getMainFormRule()
  334. repeatingFormHasValue()
  335. remark.value = flowInfo.value.seModel == '0' ? '同意' : '重新提交'
  336. })
  337. } else {
  338. getProcessFormInfo(userStore.user.useId, processInfo.insId).then(({ returnParams }) => {
  339. formElements.value = returnParams.formElements
  340. repeatingForm.value = returnParams.repeatingForm
  341. fileList.value = returnParams.fileList
  342. if (returnParams.isCancel == 1) {
  343. isCancel.value = true
  344. }
  345. repeatingFormHasValue()
  346. })
  347. }
  348. }
  349. const options = ref([])
  350. const stepActive = ref(-1)
  351. const flowInfo = ref({
  352. seModel: '0'
  353. })
  354. function initProcessFlow() {
  355. getProcessFlow(userStore.user.useId, processInfo).then(({ returnParams }) => {
  356. options.value = returnParams.list.map((item, index) => {
  357. const { tmodelName, name, createdate, finishdate, remark, state } = item
  358. if (state == 1) {
  359. stepActive.value = index
  360. }
  361. const title = tmodelName + (name == '' ? '' : ' ( ' + name + ' )')
  362. const desc = '创建时间: ' + createdate
  363. + (finishdate == '' ? '\n' : '\n办理时间: ' + finishdate)
  364. + (remark == '' ? '\n' : '\n环节意见: ' + remark)
  365. return {
  366. title,
  367. desc,
  368. state
  369. }
  370. })
  371. if (stepActive.value === -1) stepActive.value = returnParams.list.length
  372. // 获取未完成过程
  373. if (processInfo.tinsId) {
  374. getProcessFlowInfo(userStore.user.useId, processInfo).then(({ returnParams }) => {
  375. options.value.push({ title: returnParams.nextTmodels[0].nextTmodelName })
  376. flowInfo.value = returnParams.flow[0]
  377. fileList.value = [
  378. { files: returnParams.flow[0].files }
  379. ]
  380. })
  381. }
  382. })
  383. }
  384. // 下拉框
  385. function bindPickerChange(e, item) {
  386. const index = e.detail.value;
  387. item.defaultValue = item.typeDetail.enum[index].enumVname
  388. }
  389. function formatDict(dict) {
  390. return dict.map(({ enumVname }) => enumVname)
  391. }
  392. const signaturePopup = ref(null)
  393. const signatureRef = ref(null)
  394. const signaturePopupShow = ref(false)
  395. const isLandscape = ref(false)
  396. const signatureItemIndex = ref(-1)
  397. function handleSignature(index) {
  398. signatureItemIndex.value = index
  399. signaturePopup.value.open()
  400. initSignature()
  401. }
  402. function initSignature() {
  403. signaturePopupShow.value = false
  404. setTimeout(() => {
  405. signaturePopupShow.value = true
  406. }, 100)
  407. }
  408. function onclickSignatureButton(event) {
  409. // console.log('onclickSignatureButton: ', event);
  410. switch (event) {
  411. case 'undo':
  412. signatureRef.value.undo()
  413. break;
  414. case 'clear':
  415. signatureRef.value.clear()
  416. break;
  417. case 'save':
  418. signatureRef.value.canvasToTempFilePath({
  419. success: (res: LSignatureToFileSuccess) => {
  420. console.log('res: ', res);
  421. if (res.isEmpty) {
  422. $modal.msgError('签名不能为空!')
  423. return
  424. }
  425. // 判断上传文件是否是base64
  426. if (res.tempFilePath.substring(0, 'data:image/png;base64,'.length) == 'data:image/png;base64,') {
  427. const _fileData = res.tempFilePath
  428. uploadSignatureBoardImg(userStore.user.useId, _fileData, formElements.value[signatureItemIndex.value].tableField)
  429. .then(({ returnParams }) => {
  430. formElements.value[signatureItemIndex.value].defaultValue = returnParams.sealInsID
  431. formElements.value[signatureItemIndex.value].sealImgPath = returnParams.path
  432. signatureItemIndex.value = -1
  433. signaturePopupShow.value = false
  434. signaturePopup.value.close()
  435. })
  436. } else {
  437. // 转 base64
  438. wx.getFileSystemManager().readFile({
  439. filePath: res.tempFilePath,
  440. encoding: 'base64',
  441. success: fileData => {
  442. const _fileData = 'data:image/png;base64,' + fileData.data
  443. uploadSignatureBoardImg(userStore.user.useId, _fileData, formElements.value[signatureItemIndex.value].tableField)
  444. .then(({ returnParams }) => {
  445. formElements.value[signatureItemIndex.value].defaultValue = returnParams.sealInsID
  446. formElements.value[signatureItemIndex.value].sealImgPath = returnParams.path
  447. signatureItemIndex.value = -1
  448. signaturePopupShow.value = false
  449. signaturePopup.value.close()
  450. })
  451. }
  452. })
  453. }
  454. }
  455. } as LSignatureToTempFilePathOptions)
  456. break;
  457. case 'landscape':
  458. isLandscape.value = !isLandscape.value
  459. initSignature()
  460. break;
  461. }
  462. }
  463. function closeSignaturePopup() {
  464. signatureItemIndex.value = -1
  465. signaturePopupShow.value = false
  466. }
  467. const repeatingFormPopup = ref(null)
  468. function handlerepeatingForm() {
  469. repeatingFormPopup.value.open()
  470. }
  471. // 附件管理
  472. function deleteFile(file) {
  473. // console.log('deleteFile: ',file);
  474. fileList.value[0].files = fileList.value[0].files.filter(({fileId}) => !(fileId == file.fileId))
  475. }
  476. // 上传附件
  477. const subFileList = ref([]) // 文件列表
  478. const subFileSeqs = ref([])
  479. async function handleFileSelect(files) { // 新增文件
  480. // console.log('handleFileSelect', files.tempFiles)
  481. files.tempFiles.forEach(file => {
  482. const data = {
  483. name: file.name,
  484. filePath: file.path,
  485. }
  486. uploadFile(data)
  487. .then(res => {
  488. file.seq = res.returnParams
  489. subFileSeqs.value.push({ 'seq': res.returnParams, 'path': file.path })
  490. subFileList.value.push(file)
  491. })
  492. .catch(err => {
  493. $modal.msgError('文件' + data.name + '上传失败,请删除重新上传')
  494. switch (err) {
  495. case -201:
  496. console.log('文件上传失败 未找到该文件');
  497. break;
  498. case -20201:
  499. console.log('文件上传失败 返回值不是JSON字符串');
  500. break;
  501. }
  502. })
  503. })
  504. // console.log('UploadFiles', files.tempFiles);
  505. }
  506. function handleFileProgress(file, progress) {
  507. // console.log('handleFileProgress', file, progress);
  508. }
  509. function handleFileSuccess(file, res) {
  510. // console.log('handleFileSuccess', file, res);
  511. }
  512. function handleFileFail(file, err) {
  513. // console.log('handleFileFail', file, err);
  514. }
  515. function handleFileDelete(file) { // 移除文件
  516. // console.log('handleDelete', file)
  517. subFileSeqs.value.splice(subFileSeqs.value.findIndex(({ path }) => path == file.tempFilePath))
  518. }
  519. const $mainForm = ref(null)
  520. const $mainFormRules = ref({})
  521. const mainFormValue = ref({})
  522. const formatTypeDict = {
  523. '0': 'string',
  524. '1': 'number'
  525. }
  526. function validateMainForm() {
  527. }
  528. function getMainFormRule() {
  529. $mainFormRules.value = computed(() => {
  530. const obj = {};
  531. formElements.value.forEach(elem => {
  532. if (!('0' != elem.canEdit && '8' != elem.type)) return
  533. let labelName = elem.elementName
  534. let errorMessage = ''
  535. if ('13' == elem.type) errorMessage = '请完成签名'
  536. obj[elem.elementId] = {
  537. rules: [
  538. {
  539. required: '1' == elem.noNull,
  540. errorMessage,
  541. },
  542. {
  543. format: formatTypeDict[elem.fieldType] || 'string',
  544. }
  545. ],
  546. label: labelName
  547. };
  548. });
  549. return obj;
  550. }).value
  551. }
  552. const button_state = ref(true)
  553. const remark = ref('')
  554. function handleSubmitProcess(result) {
  555. let content = '确认退回'
  556. if (result == "1") {
  557. content = '确认通过'
  558. }
  559. $modal.confirm(content).then(() => {
  560. button_state.value = false
  561. if (result == "1") {
  562. mainFormValue.value = computed(() => {
  563. const obj = {};
  564. formElements.value.forEach(elem => {
  565. if (!('0' != elem.canEdit && '8' != elem.type)) return
  566. obj[elem.elementId] = elem.defaultValue;
  567. });
  568. return obj;
  569. }).value
  570. $mainForm.value.validate().then(res => {
  571. submitProcess(result)
  572. })
  573. .catch(err => {
  574. button_state.value = true
  575. $modal.msgError('表单填写错误')
  576. })
  577. } else {
  578. submitProcess(result)
  579. }
  580. }).catch(() => { })
  581. }
  582. function submitProcess(result) {
  583. formInfo.value.formElements = formElements.value
  584. // 过滤不可编辑的表单项
  585. .filter(({canEdit}) => canEdit == '1')
  586. .map(({ tableField, defaultValue }) => {
  587. return {
  588. name: tableField,
  589. value: defaultValue
  590. }
  591. })
  592. repeatingForm.value.elements.forEach((table, index) => {
  593. const newItem = table.map((item) => {
  594. item['value'] = item.defaultValue
  595. return item
  596. })
  597. formInfo.value.formElements.push(...newItem)
  598. })
  599. let flow = Object.assign({}, flowInfo.value)
  600. flow['staffId'] = userStore.user.useId
  601. flow['gxId'] = userStore.user.gxId
  602. flow['groupId'] = flowInfo.value.groupid
  603. flow['fileIds'] = subFileSeqs.value.map(({ seq }) => seq)
  604. console.log('subFileSeqs.value.map(({ seq }) => seq): ',subFileSeqs.value.map(({ seq }) => seq));
  605. // result: 1通过 2退回发起人 0退回上一级
  606. flow['result'] = result
  607. flow['remark'] = remark.value
  608. flow['nextTmodelId'] = 'undefined'
  609. // TODO 启动流程 提交时 保存删除后的文件列表
  610. submitProcessFlow(flow, formInfo.value, processInfo.control).then(({ returnMsg }) => {
  611. if (returnMsg.includes('提交失败')) {
  612. // 启用提交按钮
  613. button_state.value = true
  614. $modal.msgError(returnMsg)
  615. } else {
  616. $modal.msgSuccess(returnMsg)
  617. // 通知列表刷新数据
  618. uni.$emit('ReloadProcessData');
  619. setTimeout(() => {
  620. $tab.navigateBack();
  621. }, 1000)
  622. }
  623. })
  624. }
  625. // 取消流程
  626. function handleCancelProcess() {
  627. if (remark.value.trim() == '') {
  628. $modal.msgError('备注不能为空!')
  629. return
  630. }
  631. $modal.confirm('确认撤销').then(() => {
  632. cancelProcess()
  633. }).catch(() => { })
  634. }
  635. function cancelProcess() {
  636. cancelProcessFlow(userStore.user.useId, remark.value, processInfo).then(({ returnMsg }) => {
  637. if (returnMsg.includes('success')) {
  638. $modal.msgSuccess('撤销成功')
  639. // 通知列表刷新数据
  640. uni.$emit('ReloadProcessData');
  641. setTimeout(() => {
  642. $tab.navigateBack();
  643. }, 1000)
  644. } else {
  645. // 启用按钮
  646. button_state.value = true
  647. $modal.msgError(returnMsg)
  648. }
  649. })
  650. }
  651. </script>
  652. <style lang="scss" scoped>
  653. ::v-deep .uni-section {
  654. margin-left: -15px;
  655. margin-bottom: 10px;
  656. }
  657. .signature_container {
  658. background-color: #f5f5f5;
  659. height: 40vh;
  660. width: 90vw;
  661. .signature_content {
  662. height: 80%;
  663. width: 100%;
  664. }
  665. .signature_button_container {
  666. height: 20%;
  667. width: 100%;
  668. button {
  669. height: 100%;
  670. }
  671. }
  672. }
  673. .signature_container_landscape {
  674. height: 100vh;
  675. width: 100vw;
  676. .signature_content {
  677. height: 85%;
  678. width: 100%;
  679. }
  680. .signature_button_container {
  681. margin-top: 10%;
  682. height: 15%;
  683. width: 100%;
  684. button {
  685. height: 100%;
  686. width: 100%;
  687. transform: rotate(90deg);
  688. }
  689. }
  690. }
  691. .process_detail_container {
  692. position: relative;
  693. .repeating_table {
  694. ::v-deep .uni-forms {
  695. .uni-forms-item__label {
  696. font-size: calc(1rem + 0px) !important;
  697. line-height: calc(1rem + 0px) !important;
  698. font-weight: 600;
  699. }
  700. .uni-forms-item__content {
  701. font-size: calc(14px + .5*(1rem - 16px)) !important;
  702. .uni-easyinput__content-input {
  703. font-size: calc(14px + .5*(1rem - 16px)) !important;
  704. }
  705. }
  706. }
  707. ::v-deep .uni-forms-item--border {
  708. padding: 5px 0 !important;
  709. }
  710. .repeating_table_button_container {
  711. margin-top: 5px;
  712. button {
  713. height: 36px;
  714. line-height: 36px;
  715. color: #fff;
  716. }
  717. .add {
  718. background-color: #1ca035;
  719. }
  720. .del {
  721. background-color: #e64340;
  722. }
  723. }
  724. }
  725. .main_container {
  726. min-height: 60vh;
  727. ::v-deep .uni-forms {
  728. .uni-forms-item__label {
  729. font-size: calc(1rem + 0px) !important;
  730. line-height: calc(1rem + 0px) !important;
  731. font-weight: 600;
  732. }
  733. .uni-forms-item__content {
  734. font-size: calc(14px + .5*(1rem - 16px)) !important;
  735. .uni-easyinput__content-input {
  736. font-size: calc(14px + .5*(1rem - 16px)) !important;
  737. }
  738. }
  739. }
  740. .element_value_container {
  741. .signature_img {
  742. width: 180px;
  743. }
  744. .element_value {
  745. line-height: 36px;
  746. }
  747. }
  748. }
  749. ::v-deep .repeating_table_container {
  750. width: 98vw;
  751. .uni-table-scroll {
  752. max-height: 80vh;
  753. .uni-table {
  754. min-width: 100% !important;
  755. .uni-table-th {
  756. min-width: 50px;
  757. background-color: #2979ff;
  758. font-weight: bold;
  759. color: #fcfcfc;
  760. }
  761. }
  762. }
  763. }
  764. ::v-deep .flow_step_container {
  765. min-height: 200px;
  766. .u-steps {
  767. .u-steps-item {
  768. padding-bottom: 11px;
  769. .u-text__value--tips {
  770. font-size: calc(12px + .5*(1rem - 16px)) !important;
  771. }
  772. .redcontent {
  773. .u-text__value--content {
  774. color: #ff4500;
  775. }
  776. }
  777. .active_step_circle {
  778. width: 20px;
  779. height: 20px;
  780. box-sizing: border-box;
  781. flex-shrink: 0;
  782. border-radius: 100px;
  783. border-width: 1px;
  784. border-color: #A78BFA;
  785. background-color: #A78BFA;
  786. border-style: solid;
  787. display: flex;
  788. flex-direction: row;
  789. align-items: center;
  790. justify-content: center;
  791. transition: background-color .3s;
  792. .active_step_text {
  793. color: #fff;
  794. font-size: 11px;
  795. display: flex;
  796. flex-direction: row;
  797. align-items: center;
  798. justify-content: center;
  799. text-align: center;
  800. line-height: 11px;
  801. }
  802. }
  803. }
  804. .u-steps-item view:last-of-type {
  805. margin-top: 0 !important;
  806. .u-text__value--content {
  807. font-size: calc(16px + .5*(1rem - 16px)) !important;
  808. }
  809. .u-text__value--main {
  810. font-size: calc(16px + .5*(1rem - 16px)) !important;
  811. font-weight: 500;
  812. }
  813. }
  814. }
  815. }
  816. .approve_button {
  817. position: sticky;
  818. z-index: 10;
  819. width: 100%;
  820. bottom: 10px;
  821. }
  822. }
  823. ::v-deep button {
  824. font-size: calc(18px + .5*(1rem - 16px));
  825. }
  826. .reject_button {
  827. ::v-deep .uni-card {
  828. background-color: #f5f5f5;
  829. }
  830. }
  831. .remark_container {
  832. .remark_content {
  833. margin-bottom: 10px;
  834. ::v-deep .uni-easyinput {
  835. .uni-easyinput__content-textarea {
  836. font-size: calc(14px + .5*(1rem - 16px)) !important;
  837. }
  838. }
  839. }
  840. }
  841. </style>