Browse Source

feat(process/detail): 适配启动流程

wangpx 1 year ago
parent
commit
32cde23e00
2 changed files with 306 additions and 41 deletions
  1. 24 4
      components/ygoa/attachmentList.vue
  2. 282 37
      pages/process/detail/index.vue

+ 24 - 4
components/ygoa/attachmentList.vue

@@ -5,10 +5,19 @@
 				<view class="attachment">
 					<text @click="seeAttachment(attachment.path)" class="file_name uni-ellipsis">{{ attachment.fileName || '附件名为空' }}</text>
 					<view class="icon-container">
-						<uni-icons @click="shareAttachment(attachment.path)" :size="14" color="#2196f3" type="redo-filled"
-							style="margin-left:10px;">分享</uni-icons>
-						<uni-icons @click="copyAttachment(attachment.path)" :size="14" color="#2196f3" type="download-filled"
-							style="margin-left:10px;">复制链接</uni-icons>
+						<view>
+							<uni-icons @click="shareAttachment(attachment.path)" :size="14" color="#2196f3" type="redo-filled" style="margin-left:10px;">
+								分享
+							</uni-icons>
+							<uni-icons @click="copyAttachment(attachment.path)" :size="14" color="#2196f3" type="download-filled" style="margin-left:10px;">
+								复制链接
+							</uni-icons>
+						</view>
+						<!-- <view v-else>
+							<uni-icons @click="deleteAttachment(attachment)" :size="14" color="#ff0000" type="close" style="margin-left:10px;">
+								删除
+							</uni-icons>
+						</view> -->
 					</view>
 				</view>
 			</uni-col>
@@ -34,8 +43,15 @@
 			type: Array,
 			required: true,
 			default: []
+		},
+		canEdit:{
+			type: Boolean,
+			default: false
 		}
 	})
+	const emits = defineEmits([
+		'clickDelete', // 点击分段器
+	])
 	//检查附件列表是否有数据
 	function startAttachmentCheck() {
 		intervalId.value = setInterval(() => {
@@ -105,6 +121,10 @@
 			}
 		});
 	}
+	function deleteAttachment(file) {
+		console.log('delAttachment: ',file);
+		emits('clickDelete', file)
+	}
 </script>
 
 <style lang="scss">

+ 282 - 37
pages/process/detail/index.vue

@@ -4,19 +4,36 @@
 		<uni-card>
 			<view class="main_container">
 				<uni-section titleFontSize="1.3rem" title="申请内容" type="line"></uni-section>
-				<uni-forms label-position="left" :label-width="125" :border="true">
+				<uni-forms ref="$mainForm" :rules="$mainFormRules" :modelValue="mainFormValue" label-position="left"
+					:label-width="125" :border="true">
 					<view v-for="(elem, index) in formElements" :key="index">
 						<uni-forms-item
-							v-if="!('0' == elem.canEdit && elem.elementName.slice(-2) == '审批' && '' == elem.defaultValue) && !(undefined == elem.canEdit && elem.elementName.slice(-2) == '审批' && '' == elem.defaultValue)"
-							:label="elem.elementName">
+							v-if="!(elem.elementName.endsWith('审批') && '' == elem.defaultValue && ['0', undefined].includes(elem.canEdit))"
+							:label="elem.elementName" :name="elem.elementId">
 							<view class="element_value_container">
-								<view v-if="'1' == elem.canEdit" class="element_value">
+								<view v-if="'1' == elem.canEdit && '8' != elem.type" class="element_value">
 									<!-- 富文本框 -->
 									<uni-easyinput v-if="'1' == elem.type" placeholder="请输入内容"
 										v-model="elem.defaultValue" type="textarea"></uni-easyinput>
 									<!-- 文本框 -->
 									<uni-easyinput v-else-if="'0' == elem.type" placeholder="请输入内容"
 										v-model="elem.defaultValue" type="text"></uni-easyinput>
+									<!-- 下拉框 -->
+									<picker class="picker_container" v-else-if="'2' == elem.type"
+										@change="bindPickerChange($event, elem)" :value="elem.defaultValue"
+										:range="formatDict(elem.typeDetail.enum)">
+										<view class="uni-input input_text">
+											<!-- 设置默认值为第一个选项 -->
+											{{ elem.defaultValue ? elem.defaultValue : elem.defaultValue =
+												elem.typeDetail.enum[0].enumVname }}
+										</view>
+									</picker>
+									<!-- 年月日 时分秒 -->
+									<uni-datetime-picker v-else-if="'9' == elem.type" v-model="elem.defaultValue"
+										type="datetime" />
+									<!-- 年月日 -->
+									<uni-datetime-picker v-else-if="'3' == elem.type" v-model="elem.defaultValue"
+										type="date" />
 									<!-- 审批签字板 -->
 									<view v-else-if="'13' == elem.type">
 										<button v-if="elem.defaultValue == ''" type="primary"
@@ -29,10 +46,10 @@
 									</view>
 								</view>
 								<view
-									v-else-if="typeof elem.defaultValue === 'string' && elem.defaultValue.substring(0, 7) == '/shares'"
+									v-else-if="typeof elem.sealImgPath === 'string' && elem.sealImgPath.startsWith('/shares')"
 									class="signature_img">
 									<img style="width: 100%;" mode="widthFix"
-										:src="config.baseUrlPre + elem.defaultValue" />
+										:src="config.baseUrlPre + elem.sealImgPath" />
 								</view>
 								<text class="element_value" v-else>{{ elem.defaultValue }}</text>
 							</view>
@@ -41,12 +58,32 @@
 				</uni-forms>
 			</view>
 		</uni-card>
-		<view>
+		<view class="repeating_table">
 			<!-- 重复表 -->
-			<uni-card v-if="repeatingFormNotEmpty" spacing="0" padding="0">
+			<uni-card v-if="flowInfo.seModel == '0' && repeatingFormNotEmpty" spacing="0" padding="0">
 				<button @click="handlerepeatingForm" type="primary">查看明细</button>
 			</uni-card>
-			<uni-popup v-if="repeatingFormNotEmpty" ref="repeatingFormPopup">
+			<uni-card v-else v-for="(table, tableIndex) in repeatingForm.elements" spacing="0" :key="tableIndex">
+				<uni-forms label-position="left" :label-width="125" :border="true">
+					<uni-forms-item :name="elem.tableField" v-for="(elem, itemIndex) in table"
+						:label="repeatingForm.elementItem[itemIndex].elementName.slice(3)" :key="itemIndex">
+						<uni-easyinput placeholder="请输入内容" v-model="elem.defaultValue"
+							:type="fieldTypeDict[repeatingForm.elementItem[itemIndex].fieldType.value] || 'text'"></uni-easyinput>
+					</uni-forms-item>
+				</uni-forms>
+				<!-- <view class="repeating_table_button_container">
+					<uni-row>
+						<uni-col :span="10" :offset="1">
+							<button @click="addrepeatingFormItem(tableIndex)" type="primary">新增</button>
+						</uni-col>
+						<uni-col :span="10" :offset="2">
+							<button @click="delrepeatingFormItem(tableIndex)"
+								:disabled="repeatingForm.elements.length <= 1" type="warn">删除</button>
+						</uni-col>
+					</uni-row>
+				</view> -->
+			</uni-card>
+			<uni-popup v-if="flowInfo.seModel == '0' && repeatingFormNotEmpty" ref="repeatingFormPopup">
 				<uni-card margin="0px" spacing="0px" padding="0px">
 					<view class="repeating_table_container">
 						<uni-table :border="true" stripe>
@@ -55,15 +92,17 @@
 									{{ item.elementName.slice(3, 5) }}
 								</uni-th>
 							</uni-tr>
-							<uni-tr
-								v-for="(item, index) in (repeatingForm.elements.length / repeatingForm.elementItem.length)"
-								:key="index">
-								<uni-td align="center" v-for="col in repeatingForm.elementItem.length" :key="col">
-									<!-- (列数 - 1) * 总行数 + 当前行数 -->
-									{{ repeatingForm.elements[(col - 1) * (repeatingForm.elements.length /
-										repeatingForm.elementItem.length) + index]!.defaultValue }}
+							<uni-tr v-for="(table, tableIndex) in repeatingForm.elements" :key="tableIndex">
+								<uni-td align="center" v-for="(elem, itemIndex) in table" :key="itemIndex">
+									{{ elem.defaultValue }}
 								</uni-td>
 							</uni-tr>
+							<!-- (列数 - 1) * 总行数 + 当前行数 -->
+							<!-- <uni-tr v-for="(item, index) in (repeatingForm.elements.length / repeatingForm.elementItem.length)" :key="index">
+								<uni-td align="center" v-for="col in repeatingForm.elementItem.length" :key="col">
+									{{ repeatingForm.elements[(col - 1) * (repeatingForm.elements.length / repeatingForm.elementItem.length) + index]!.defaultValue }}
+								</uni-td>
+							</uni-tr> -->
 						</uni-table>
 					</view>
 				</uni-card>
@@ -73,7 +112,15 @@
 		<view v-for="(item, index) in fileList" :key="index">
 			<uni-card v-if="item.files.length != undefined && item.files.length > 0">
 				<uni-section titleFontSize="1.3rem" title="附件" type="line"></uni-section>
-				<attachment-list :attachments="item.files"></attachment-list>
+				<attachment-list @clickDelete="deleteFile" :canEdit="flowInfo.seModel == '1'" :attachments="item.files"></attachment-list>
+			</uni-card>
+		</view>
+		<!-- 上传附件 -->
+		<view v-if="processInfo.tinsId || flowInfo.seModel == '1'" class="file_picker_container">
+			<uni-card title="上传附件" :extra="`${subFileList.length}/15`" spacing="0">
+				<uni-file-picker ref="filePicker" v-model="subFileList" :auto-upload="true" mode="list" limit="10"
+					file-mediatype="all" @select="handleFileSelect" @progress="handleFileProgress"
+					@success="handleFileSuccess" @fail="handleFileFail" @delete="handleFileDelete" />
 			</uni-card>
 		</view>
 		<!-- 流转过程 -->
@@ -114,11 +161,13 @@
 		<view v-if="processInfo.tinsId" class="approve_button">
 			<uni-card spacing="0" padding="0">
 				<button :disabled="!button_state" :loading="!button_state" type="primary"
-					@click="handleSubmitProcess('1')">通过</button>
+					@click="handleSubmitProcess('1')">
+					{{ flowInfo.seModel == '0' ? '通过' : '提交' }}
+				</button>
 			</uni-card>
 		</view>
 	</view>
-	<view v-if="processInfo.tinsId">
+	<view v-if="processInfo.tinsId && flowInfo.seModel == '0'">
 		<view class="reject_button">
 			<uni-card spacing="0" padding="0" :is-shadow="false" :border="false">
 				<uni-row>
@@ -153,7 +202,7 @@
 	<view style="height: 5px; margin-top: 20px;"></view>
 	<!-- 签字版弹出层 -->
 	<uni-popup ref="signaturePopup" @maskClick="closeSignaturePopup">
-		<view class="signature_container" :class="isLandscape ? ' signature_container_landscape' : ''">
+		<view class="signature_container" :class="{ 'signature_container_landscape': isLandscape }">
 			<view class="signature_content">
 				<l-signature ref="signatureRef" v-if="signaturePopupShow" :landscape="isLandscape" :penSize="8"
 					:minLineWidth="4" :maxLineWidth="12" :openSmooth="true" :preferToDataURL="true"
@@ -180,15 +229,19 @@
 </template>
 
 <script setup lang="ts">
-import { onMounted, reactive, ref } from 'vue'
+import { computed, onMounted, reactive, ref } from 'vue'
 import { onLoad } from '@dcloudio/uni-app'
 import { LSignatureToTempFilePathOptions, LSignatureToFileSuccess } from '@/uni_modules/lime-signature'
 import attachmentList from '@/components/ygoa/attachmentList.vue'
 import config from '@/config.js'
 import $tab from '@/plugins/tab.js'
 import $modal from '@/plugins/modal.js'
-import { getProcessFlowInfo, getProcessFormInfo, getProcessFormInfoInFlow, getProcessFlow, submitProcessFlow, cancelProcessFlow, uploadSignatureImg, uploadSignatureBoardImg } from '@/api/process.js'
+import { getProcessFlowInfo, getProcessFormInfo, getProcessFormInfoInFlow, getProcessFlow, submitProcessFlow, cancelProcessFlow, uploadSignatureImg, uploadSignatureBoardImg, uploadFile } from '@/api/process.js'
 import { useUserStore } from '@/store/user.js'
+const fieldTypeDict = {
+	'0': 'text',
+	'1': 'number'
+}
 const userStore = useUserStore()
 const processInfo = reactive({
 	insId: '',
@@ -233,7 +286,35 @@ function repeatingFormHasValue() {
 			})
 		}
 	})
-
+}
+// 新增重复表表单
+function addrepeatingFormItem(index) {
+	// parseCalculation(repeatingForm.value.elementItem)
+	const table = repeatingForm.value.elementItem.map(({ tableField, bddzText }) => {
+		const item = {
+			name: tableField,
+			defaultValue: ""
+		}
+		if (bddzText != '') {
+			const mulItem = bddzText.split('*')
+			// 保存关联数据索引
+			item['bddzText'] = mulItem.map(item => {
+				// item['operation '].operands = mulItem.map(item => {
+				return repeatingForm.value.elementItem.findIndex(({ elementName }) => item == elementName)
+			})
+		}
+		return item
+	})
+	repeatingForm.value.elements.splice(index + 1, 0, table)
+}
+// 删除重复表表单
+function delrepeatingFormItem(index) {
+	console.log("delrepeatingFormItem: " + index);
+	$modal.confirm('', '确认删除该重复表数据')
+		.then(() => {
+			repeatingForm.value.elements.splice(index, 1)
+			// $repeatingForms.value.splice(index, 1)
+		}).catch(() => { })
 }
 const formElements = ref([])
 const formInfo = ref({
@@ -249,8 +330,9 @@ function initProcessInfo() {
 			formElements.value = returnParams.formElements
 			formInfo.value = returnParams.formInfo[0]
 			repeatingForm.value = returnParams.repeatingForm
+			getMainFormRule()
 			repeatingFormHasValue()
-			remark.value = '同意'
+			remark.value = flowInfo.value.seModel == '0' ? '同意' : '重新提交'
 		})
 	} else {
 		getProcessFormInfo(userStore.user.useId, processInfo.insId).then(({ returnParams }) => {
@@ -266,7 +348,9 @@ function initProcessInfo() {
 }
 const options = ref([])
 const stepActive = ref(-1)
-const flowInfo = ref({})
+const flowInfo = ref({
+	seModel: '0'
+})
 function initProcessFlow() {
 	getProcessFlow(userStore.user.useId, processInfo).then(({ returnParams }) => {
 		options.value = returnParams.list.map((item, index) => {
@@ -298,6 +382,15 @@ function initProcessFlow() {
 	})
 }
 
+// 下拉框
+function bindPickerChange(e, item) {
+	const index = e.detail.value;
+	item.defaultValue = item.typeDetail.enum[index].enumVname
+}
+function formatDict(dict) {
+	return dict.map(({ enumVname }) => enumVname)
+}
+
 const signaturePopup = ref(null)
 const signatureRef = ref(null)
 const signaturePopupShow = ref(false)
@@ -315,7 +408,7 @@ function initSignature() {
 	}, 100)
 }
 function onclickSignatureButton(event) {
-	console.log('onclickSignatureButton: ', event);
+	// console.log('onclickSignatureButton: ', event);
 	switch (event) {
 		case 'undo':
 			signatureRef.value.undo()
@@ -360,7 +453,6 @@ function onclickSignatureButton(event) {
 							}
 						})
 					}
-					console.log('formElements', formElements.value[signatureItemIndex.value]);
 				}
 			} as LSignatureToTempFilePathOptions)
 			break;
@@ -380,7 +472,90 @@ const repeatingFormPopup = ref(null)
 function handlerepeatingForm() {
 	repeatingFormPopup.value.open()
 }
+// 附件管理
+function deleteFile(file) {
+	// console.log('deleteFile: ',file);
+	fileList.value[0].files = fileList.value[0].files.filter(({fileId}) => !(fileId == file.fileId))
+}
+// 上传附件
+const subFileList = ref([]) // 文件列表
+const subFileSeqs = ref([])
+async function handleFileSelect(files) { // 新增文件
+	// console.log('handleFileSelect', files.tempFiles)
+	files.tempFiles.forEach(file => {
+		const data = {
+			name: file.name,
+			filePath: file.path,
+		}
+		uploadFile(data)
+			.then(res => {
+				file.seq = res.returnParams
+				subFileSeqs.value.push({ 'seq': res.returnParams, 'path': file.path })
+				subFileList.value.push(file)
+			})
+			.catch(err => {
+				$modal.msgError('文件' + data.name + '上传失败,请删除重新上传')
+				switch (err) {
+					case -201:
+						console.log('文件上传失败 未找到该文件');
+						break;
+					case -20201:
+						console.log('文件上传失败 返回值不是JSON字符串');
+						break;
+				}
+			})
+	})
+	// console.log('UploadFiles', files.tempFiles);
+
+}
+function handleFileProgress(file, progress) {
+	// console.log('handleFileProgress', file, progress);
+}
+function handleFileSuccess(file, res) {
+	// console.log('handleFileSuccess', file, res);
+}
+function handleFileFail(file, err) {
+	// console.log('handleFileFail', file, err);
+}
+function handleFileDelete(file) { // 移除文件
+	// console.log('handleDelete', file)
+	subFileSeqs.value.splice(subFileSeqs.value.findIndex(({ path }) => path == file.tempFilePath))
+}
+
+const $mainForm = ref(null)
+const $mainFormRules = ref({})
+const mainFormValue = ref({})
+const formatTypeDict = {
+	'0': 'string',
+	'1': 'number'
+}
+function validateMainForm() {
 
+}
+function getMainFormRule() {
+	$mainFormRules.value = computed(() => {
+		const obj = {};
+		formElements.value.forEach(elem => {
+			if (!('0' != elem.canEdit && '8' != elem.type)) return
+			let labelName = elem.elementName
+			let errorMessage = ''
+			if ('13' == elem.type) errorMessage = '请完成签名'
+			obj[elem.elementId] = {
+				rules: [
+					{
+						required: '1' == elem.noNull,
+						errorMessage,
+					},
+					{
+						format: formatTypeDict[elem.fieldType] || 'string',
+					}
+				],
+				label: labelName
+			};
+		});
+		return obj;
+	}).value
+}
 const button_state = ref(true)
 const remark = ref('')
 function handleSubmitProcess(result) {
@@ -389,26 +564,56 @@ function handleSubmitProcess(result) {
 		content = '确认通过'
 	}
 	$modal.confirm(content).then(() => {
-		submitProcess(result)
+		button_state.value = false
+		if (result == "1") {
+			mainFormValue.value = computed(() => {
+				const obj = {};
+				formElements.value.forEach(elem => {
+					if (!('0' != elem.canEdit && '8' != elem.type)) return
+					obj[elem.elementId] = elem.defaultValue;
+				});
+				return obj;
+			}).value
+			$mainForm.value.validate().then(res => {
+				submitProcess(result)
+			})
+				.catch(err => {
+					button_state.value = true
+					$modal.msgError('表单填写错误')
+				})
+		} else {
+			submitProcess(result)
+		}
 	}).catch(() => { })
 }
 function submitProcess(result) {
-	button_state.value = false
-	formInfo.value.formElements = formElements.value.map(({ tableField, defaultValue }) => {
-		return {
-			name: tableField,
-			value: defaultValue
-		}
+	formInfo.value.formElements = formElements.value
+		// 过滤不可编辑的表单项
+		.filter(({canEdit}) => canEdit == '1')
+		.map(({ tableField, defaultValue }) => {
+			return {
+				name: tableField,
+				value: defaultValue
+			}
+		})
+	repeatingForm.value.elements.forEach((table, index) => {
+		const newItem = table.map((item) => {
+			item['value'] = item.defaultValue
+			return item
+		})
+		formInfo.value.formElements.push(...newItem)
 	})
 	let flow = Object.assign({}, flowInfo.value)
 	flow['staffId'] = userStore.user.useId
 	flow['gxId'] = userStore.user.gxId
 	flow['groupId'] = flowInfo.value.groupid
-	flow['fileIds'] = ''
+	flow['fileIds'] = subFileSeqs.value.map(({ seq }) => seq)
+	console.log('subFileSeqs.value.map(({ seq }) => seq): ',subFileSeqs.value.map(({ seq }) => seq));
 	// result: 1通过 2退回发起人 0退回上一级
 	flow['result'] = result
 	flow['remark'] = remark.value
 	flow['nextTmodelId'] = 'undefined'
+	// TODO 启动流程 提交时 保存删除后的文件列表
 	submitProcessFlow(flow, formInfo.value, processInfo.control).then(({ returnMsg }) => {
 		if (returnMsg.includes('提交失败')) {
 			// 启用提交按钮
@@ -424,7 +629,7 @@ function submitProcess(result) {
 		}
 	})
 }
-
+// 取消流程
 function handleCancelProcess() {
 	if (remark.value.trim() == '') {
 		$modal.msgError('备注不能为空!')
@@ -503,6 +708,46 @@ function cancelProcess() {
 .process_detail_container {
 	position: relative;
 
+	.repeating_table {
+		::v-deep .uni-forms {
+			.uni-forms-item__label {
+				font-size: calc(1rem + 0px) !important;
+				line-height: calc(1rem + 0px) !important;
+				font-weight: 600;
+			}
+
+			.uni-forms-item__content {
+				font-size: calc(14px + .5*(1rem - 16px)) !important;
+
+				.uni-easyinput__content-input {
+					font-size: calc(14px + .5*(1rem - 16px)) !important;
+				}
+			}
+		}
+
+		::v-deep .uni-forms-item--border {
+			padding: 5px 0 !important;
+		}
+
+		.repeating_table_button_container {
+			margin-top: 5px;
+
+			button {
+				height: 36px;
+				line-height: 36px;
+				color: #fff;
+			}
+
+			.add {
+				background-color: #1ca035;
+			}
+
+			.del {
+				background-color: #e64340;
+			}
+		}
+	}
+
 	.main_container {
 		min-height: 60vh;
 
@@ -637,7 +882,7 @@ function cancelProcess() {
 
 		::v-deep .uni-easyinput {
 			.uni-easyinput__content-textarea {
-				font-size:  calc(14px + .5*(1rem - 16px)) !important;
+				font-size: calc(14px + .5*(1rem - 16px)) !important;
 			}
 		}
 	}