ソースを参照

feat: 仓库出库审批

liujt 4 日 前
コミット
1caf745cd3

+ 9 - 0
api/warehouseManagement/index.js

@@ -166,6 +166,15 @@ export async function getInboundDetailsById(id) {
 	return Promise.reject(new Error(res.message))
 }
 
+//多ID获取出入库详情
+export async function getInboundDetailsByIds(data) {
+    const res = await postJ(Vue.prototype.apiUrl + `/wms/outintwo/getByIds`, data);
+    if (res.code == 0) {
+      return res.data;
+    }
+    return Promise.reject(new Error(res.message));
+}
+
 // 获取入库详情(出库单号)
 export async function getInboundDetailsByBizNo(id) {
 	const res = await get(Vue.prototype.apiUrl + `/wms/outintwo/getInfoByBizNo/${id}`)

+ 15 - 0
enum/dict.js

@@ -120,6 +120,21 @@ export const requirementSourceType = [
   },
 ];
 
+// 质检状态 0未检 1已检
+export const qualityStatus = {
+  0: '未质检',
+  1: '待检',
+  2: '已质检'
+};
+
+// 质检结果 0无 1合格 2不合格
+export const qualityResults = {
+  0: '无',
+  1: '合格',
+  2: '不合格',
+  3: '让步接收'
+};
+
 // 列表维度
 export const dimensionType = [
   {

+ 8 - 0
pages.json

@@ -132,6 +132,14 @@
 				"navigationBarTextStyle": "white"
 			}
 		},
+		{ // 出库审批
+			"path": "pages/home/wt/components/outBound/processTask",
+			"style": {
+				"navigationBarTitleText": "",
+				"navigationStyle": "custom",
+				"navigationBarTextStyle": "white"
+			}
+		},
 		{ // 销售发货确认
 			"path": "pages/home/wt/components/salesInvoiceConfirm/processTask",
 			"style": {

+ 188 - 0
pages/home/wt/components/outBound/processTask.vue

@@ -0,0 +1,188 @@
+<template>
+	<view class="havedone-container">
+		<uni-nav-bar fixed="true" statusBar="true" left-icon="back" :title="uniNavBarTitle"
+		 background-color="#157A2C" color="#fff"
+			@clickLeft="back"></uni-nav-bar>
+			<!-- <iframe src="http://aiot.zoomwin.com.cn:51001/test/a.html" style="width: 200px;height: 600px" frameborder="0"></iframe> -->
+		<view v-if="processInstance.processDefinition">
+			<taskForm id='async-biz-form-component' :taskId="listData.taskId" :businessId="listData.businessId" :id="listData.id"
+				:taskDefinitionKey="listData.taskDefinitionKey"
+				 ref="bziRef"></taskForm>
+				 
+		</view>
+		<view v-if="listData.type !='view'">
+			<view v-for="(item, index) in runningTasks" :key="index">
+				<div v-if="processInstance.processDefinition">
+					<taskSubmit id='async-sub-form-component' :taskId="listData.taskId" :businessId="listData.businessId" :id="listData.id"
+						:taskDefinitionKey="listData.taskDefinitionKey" @handleAudit="handleAudit"
+						@getTableValue="getTableValue" @handleUpdateAssignee="handleUpdateAssignee(item)"
+						@handleBackList="handleBackList(item)" ref="subForm">
+					</taskSubmit>
+				</div>
+			</view>
+		</view>
+		<u-toast ref="uToast"></u-toast>
+	</view>
+</template>
+
+<script>
+	import {
+		getProcessInstance,
+		getTaskListByProcessInstanceId
+	} from '@/api/wt/index.js'
+import Vue from 'vue'
+import taskForm from './taskForm.vue'
+import taskSubmit from './taskSubmit.vue'
+
+	export default {
+		name: 'processTask',
+		components:{ taskForm,taskSubmit },
+		data() {
+			return {
+				uniNavBarTitle: '',
+				processInstanceLoading: false,
+				listData: {},
+				processInstance: {},
+				runningTasks: [],
+				auditForms: [],
+				activeComp: null,
+			}
+		},
+
+		onLoad(option) {
+			this.listData = option
+			this.getDetail()
+			 this.activeComp = 'tab1'
+		},
+		methods: {
+			/** 获得流程实例 */
+		async	getDetail() {
+				// 获得流程实例相关
+				this.processInstanceLoading = true;
+				getProcessInstance({
+					id: this.listData.id
+				}).then(async (response) => {
+					if (!response) {
+						this.$message.error('查询不到流程信息!');
+						return;
+					}
+					// 设置流程信息 
+						this.processInstance = response;
+					this.uniNavBarTitle =`${ response.name } 【${ response.startUser?.nickname}】`
+						
+			
+					// //将业务表单,注册为动态组件
+					// Vue.component('async-biz-form-component', (resolve) => {
+					// 	require(['pages/home' + this.listData.miniHandleRouter], resolve);
+					// });
+					// Vue.component('async-sub-form-component', (resolve) => {
+					// 	require(['pages/home' + this.listData.miniViewRouter], resolve);
+					// });
+					
+				
+					this.processInstanceLoading = false;
+				});
+
+				this.runningTasks = [];
+				this.auditForms = [];
+				getTaskListByProcessInstanceId({
+					processInstanceId: this.listData.id
+				}).then((response) => {
+					console.log(response, 'response');
+					// 审批记录
+					this.tasks = [];
+					// 移除已取消的审批
+					response.forEach((task) => {
+						if (task.result !== 4) {
+							this.tasks.push(task);
+						}
+					});
+					// 排序,将未完成的排在前面,已完成的排在后面;
+					this.tasks.sort((a, b) => {
+						// 有已完成的情况,按照完成时间倒序
+						if (a.endTime && b.endTime) {
+							return b.endTime - a.endTime;
+						} else if (a.endTime) {
+							return 1;
+						} else if (b.endTime) {
+							return -1;
+							// 都是未完成,按照创建时间倒序
+						} else {
+							return b.createTime - a.createTime;
+						}
+					});
+
+					// 需要审核的记录
+					let userInfo = wx.getStorageSync("userInfo");
+					this.tasks.forEach((task) => {
+						if (task.result !== 1 && task.result !== 6) {
+							// 只有待处理才需要
+							return;
+						}
+						if (!task.assigneeUser || task.assigneeUser.id !== userInfo.userId) {
+							// 自己不是处理人
+							return;
+						}
+						if (task.taskDefinitionKey !== this.listData.taskDefinitionKey) {
+							// 不是当前流程的
+							return;
+						}
+						this.runningTasks.push({
+							...task
+						});
+						console.log(this.runningTasks, ' this.runningTasks');
+						this.auditForms.push({
+							reason: ''
+						});
+					});
+				});
+			},
+
+
+			/** 处理审批通过和不通过的操作 */
+			handleAudit(data) {
+				let text = data.status === 1 ? '通过' : '不通过';
+				this.$refs.uToast.show({
+					type: 'success',
+					message: `审批${data.title || text}成功!`,
+					iconUrl: 'https://cdn.uviewui.com/uview/demo/toast/success.png'
+				})
+				// 获得最新详情
+				setTimeout(() => {
+				
+					uni.navigateBack()
+				}, 1000)
+
+
+				// const index = this.runningTasks.indexOf(task);
+				// this.$refs['form' + index][0].validate((valid) => {
+				//   if (!valid) {
+				//     return;
+				//   }
+				//   const data = {
+				//     id: task.id,
+				//     reason: this.auditForms[index].reason
+				//   };
+				//   if (pass) {
+				//     approveTask(data).then((response) => {
+				//       this.$message.success('审批通过成功!');
+				//       this.handleClose(); // 获得最新详情
+				//     });
+				//   } else {
+				//     rejectTask(data).then((response) => {
+				//       this.$message.success('审批不通过成功!');
+				//       this.handleClose(); // 获得最新详情
+				//     });
+				//   }
+				// });
+			},
+			getTableValue(fn) {
+				fn(this.$refs.bziRef.getTableValue());
+			}
+		}
+
+	}
+</script>
+
+<style>
+</style>

+ 308 - 0
pages/home/wt/components/outBound/taskForm.vue

@@ -0,0 +1,308 @@
+<template>
+	<view class="task-form-container">
+		<u-sticky offset-top="50">
+			<u-subsection fontSize="25" mode="subsection" :list="tabList" :current="curNow" @change="sectionChange"
+				activeColor="#157A2C" bgColor="#fff"></u-subsection>
+		</u-sticky>
+
+		<!-- 出库信息 -->
+		<view v-show="curNow === 0">
+			<u--form style="margin: 0 20px;" labelPosition="left" :model="infoData" ref="uForm" labelWidth="180rpx">
+				<u-form-item label="出库单号" borderBottom>
+					{{ infoData.bizNo || '-' }}
+				</u-form-item>
+				<u-form-item label="出库物品类型" borderBottom>
+					{{ handleAssetType((infoData.extInfo && infoData.extInfo.assetType)) || '-' }}
+				</u-form-item>
+				<u-form-item label="出库场景" borderBottom>
+					{{ getSceneState(infoData.bizType) || '-' }}
+				</u-form-item>
+				<u-form-item label="关联订单" borderBottom>
+					{{ (extInfo && extInfo.documentSource) || '-' }}
+				</u-form-item>
+				<u-form-item label="来源单据" borderBottom>
+					{{ infoData.sourceBizNo || '-' }}
+				</u-form-item>
+				<template v-if="infoData.bizType == 3">
+					<u-form-item label="客户名称" borderBottom>
+						{{ infoData.clientName || '-' }}
+					</u-form-item>
+					<u-form-item label="客户联系人" borderBottom>
+						{{ infoData.clientUser || '-' }}
+					</u-form-item>
+					<u-form-item label="客户电话" borderBottom>
+						{{ infoData.clientPhone || '-' }}
+					</u-form-item>
+				</template>
+				<u-form-item label="领料人" borderBottom>
+					{{ infoData.fromUser || '-' }}
+				</u-form-item>
+				<u-form-item label="领料人联系方式" borderBottom>
+					{{ infoData.fromUserPhone || '-' }}
+				</u-form-item>
+				<u-form-item label="领料人部门" borderBottom>
+					{{ (infoData.extInfo && infoData.extInfo.verifyDeptName) || '-' }}
+				</u-form-item>
+				<u-form-item label="出库时间" borderBottom>
+					{{ infoData.storageTime || infoData.createTime || '-' }}
+				</u-form-item>
+				<u-form-item label="出库登记人" borderBottom>
+					{{ (infoData.extInfo && infoData.extInfo.createUserName) || '-' }}
+				</u-form-item>
+				<u-form-item label="权属部门" borderBottom>
+					{{ (infoData.extInfo && infoData.extInfo.deptName) || '-' }}
+				</u-form-item>
+				<u-form-item label="状态" borderBottom>
+					{{ stepsTitle }}
+				</u-form-item>
+				<u-form-item label="备注" borderBottom>
+					{{ infoData.remark || '-' }}
+				</u-form-item>
+			</u--form>
+		</view>
+
+		<!-- 出库物品清单 -->
+		<view v-show="curNow === 1">
+			<common-product-list :list="productList" :tableField="productFields"></common-product-list>
+		</view>
+
+		<!-- 包装明细 -->
+		<view v-show="curNow === 2">
+			<common-product-list :list="showPackingList" :tableField="packingFields"></common-product-list>
+		</view>
+	</view>
+</template>
+
+<script>
+	import { getInboundDetailsById, getInboundDetailsByIds } from '@/api/warehouseManagement'
+	import { getInfoBySourceBizNoAll } from '@/api/wms'
+	import { allCategoryLevel } from '@/api/wt';
+	import { useDictLabel } from '@/utils/dict/index';
+	import { outputSceneState, qualityResults, qualityStatus } from '@/enum/dict.js';
+	import { mapGetters, mapActions } from 'vuex';
+	import { parameterGetByCode } from '@/api/mainData/index.js';
+	import commonProductList from '../common/commonProductList.vue';
+
+	export default {
+		components: { commonProductList },
+		props: {
+			businessId: {
+				type: String,
+				default: ''
+			},
+			isInterior: {
+				type: Boolean,
+				default: true
+			},
+			isIds: {
+				type: Boolean,
+				default: false
+			},
+			XTBG: {
+				type: Boolean,
+				default: false
+			},
+		},
+		data() {
+			return {
+				tabList: ['出库信息', '物品清单', '包装明细'],
+				curNow: 0,
+				qualityStatus,
+				qualityResults,
+				productList: [],
+				showPackingList: [],
+				packingList: [],
+				extInfo: {},
+				codeList: [],
+				infoData: {},
+				stepsTitle: '已完成',
+				stepsStatus: 'success',
+				active: 0,
+				isPrice: 1,
+				// 物品清单字段配置
+				productFields: [
+					{ label: '仓库', field: 'warehouseName' },
+					{ label: '编码', field: 'categoryCode' },
+					{ label: '名称', field: 'categoryName' },
+					{ label: '型号', field: 'categoryModel' },
+					{ label: '规格', field: 'specification' },
+					{ label: '牌号', field: 'brandNum' },
+					{ label: '批次号', field: 'batchNo' },
+					{ label: '包装数量', field: 'packingQuantity' },
+					{ label: '单位', field: 'packingUnit' },
+					{ label: '单价', field: 'singlePrice' },
+					{ label: '计量数量', field: 'measureQuantity' },
+					{ label: '计量单位', field: 'measureUnit' },
+					{ label: '重量', field: 'weight' },
+					{ label: '重量单位', field: 'weightUnit' },
+					{ label: '机型', field: 'modelKey' },
+					{ label: '颜色', field: 'colorKey' },
+					{ label: '锁定数量', field: 'lockQuantity' },
+					{ label: '库存', field: 'stockNum' },
+				],
+				// 包装明细字段配置
+				packingFields: [
+					{ label: '编码', field: 'categoryCode' },
+					{ label: '名称', field: 'categoryName' },
+					{ label: '批次号', field: 'batchNo' },
+					{ label: '发货条码', field: 'barcodes' },
+					{ label: '包装编码', field: 'packageNo' },
+					{ label: '包装数量', field: 'packingQuantity' },
+					{ label: '单位', field: 'packingUnit' },
+					{ label: '计量数量', field: 'measureQuantity' },
+					{ label: '计量单位', field: 'measureUnit' },
+					{ label: '物料代号', field: 'materielDesignation' },
+					{ label: '客户代号', field: 'clientCode' },
+					{ label: '刻码', field: 'engrave' },
+					{ label: '重量', field: 'weight' },
+					{ label: '重量单位', field: 'weightUnit' },
+					{ label: '机型', field: 'modelKey' },
+					{ label: '颜色', field: 'colorKey' },
+					{ label: '供应商', field: 'supplierName' },
+					{ label: '供应商代号', field: 'supplierCode' },
+					{ label: '质检结果', field: 'result', type: 'qualityResult' },
+					{ label: '质检状态', field: 'status', type: 'qualityStatus' },
+					{ label: '生产日期', field: 'productionDate' },
+					{ label: '采购日期', field: 'purchaseDate' },
+				],
+			};
+		},
+		watch: {
+			'infoData.verifyStatus': {
+				immediate: true,
+				handler(val) {
+					if (val == 0) {
+						this.active = 1;
+						this.stepsTitle = '未审核';
+						this.stepsStatus = 'wait';
+					} else if (val == 1) {
+						this.active = 2;
+						this.stepsTitle = '审核中';
+						this.stepsStatus = 'process';
+					} else if (val == 2) {
+						this.active = 2;
+						this.stepsTitle = '审核通过';
+						this.stepsStatus = 'success';
+					} else if (val == 3) {
+						this.active = 2;
+						this.stepsTitle = '驳回';
+						this.stepsStatus = 'error';
+					}
+				}
+			},
+		},
+		computed: {
+			...mapGetters(['getDictValue']),
+			clientEnvironmentId() {
+				return this.$store.state.user.info.clientEnvironmentId;
+			},
+		},
+		created() {
+			// 仓库出入库是否显示金额
+			parameterGetByCode({
+				code: 'wms_price'
+			}).then((res) => {
+				this.isPrice = res.value;
+			});
+			this.requestDict('类型用途');
+			this.getAllCategoryType();
+			this._getInfo(this.businessId);
+		},
+		methods: {
+			...mapActions('dict', ['requestDict']),
+			getSceneState: useDictLabel(outputSceneState),
+			sectionChange(index) {
+				this.curNow = index;
+			},
+			handleAssetType(r) {
+				const code = this.codeList.find((item) => item.dictCode == r);
+				return (code && code.dictValue);
+			},
+			async getAllCategoryType() {
+				const data = await allCategoryLevel();
+				console.log('getAllCategoryType', data);
+				this.codeList = data.map((item) => {
+					return { dictCode: item.id, dictValue: item.name };
+				});
+			},
+			async _getInfo(id) {
+				if (!id) return;
+				let res = {};
+				let resAll = [];
+				if (this.isInterior) {
+					res = await getInboundDetailsById(id);
+				} else if (this.isIds) {
+					resAll = await getInboundDetailsByIds(id);
+				} else {
+					res = await getInfoBySourceBizNoAll(id);
+					res = res[0] || {};
+				}
+				if (this.isIds) {
+					res = JSON.parse(JSON.stringify(resAll[0]));
+					this.extInfo = resAll[0].extInfo;
+					res['outInDetailList'] = [];
+					resAll.forEach((item) => {
+						item.outInDetailList.forEach((val) => {
+							val['bizNo'] = item.bizNo;
+							res['outInDetailList'].push(val);
+						});
+					});
+					res['bizNo'] = resAll.map((item) => item.bizNo);
+					this.infoData = res;
+				} else {
+					this.infoData = res;
+					this.extInfo = res && res.extInfo;
+				}
+				this.productList = (res && res.outInDetailList && res.outInDetailList.map((productItem) => {
+					return {
+						...productItem,
+						productCode: productItem.categoryCode,
+						productName: productItem.categoryName,
+						outInDetailRecordRequestList:
+							(productItem.outInDetailRecordRequestList && productItem.outInDetailRecordRequestList.map((packingItem) => {
+								return {
+									...packingItem,
+									categoryName: productItem.categoryName,
+									categoryCode: productItem.categoryCode,
+									supplierCode: productItem.supplierCode,
+									supplierName: productItem.supplierName,
+									materialDetailList: (packingItem.materialDetailList && packingItem.materialDetailList.map((materialItem) => {
+										return {
+											...materialItem,
+											categoryName: productItem.categoryName,
+											categoryCode: productItem.categoryCode
+										};
+									}))
+								};
+							}))
+					};
+				})) || [];
+				// 获取包装维度数据,预处理质检字段为文本
+				const arr = [];
+				this.productList.forEach((item) => {
+					(item.outInDetailRecordRequestList || []).forEach((k) => {
+						arr.push({
+							...k,
+							productCode: k.packageNo || k.categoryCode,
+							productName: k.categoryName || '包装',
+							result: qualityResults[k.result] != null ? qualityResults[k.result] : k.result,
+							status: qualityStatus[k.status] != null ? qualityStatus[k.status] : k.status,
+						});
+					});
+				});
+				this.packingList = arr;
+				this.showPackingList = arr;
+			},
+			getTableValue() {
+				return {};
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.task-form-container {
+		min-height: 100vh;
+		background-color: #fff;
+	}
+</style>

+ 165 - 0
pages/home/wt/components/outBound/taskSubmit.vue

@@ -0,0 +1,165 @@
+<template>
+	<view class="">
+		<u--form style="margin: 0 20px;" labelPosition="left" :model="form" :rules="rules" ref="uForm"
+			labelWidth='140rpx'>
+			<u-form-item label="审批建议" prop="reason" required>
+				<u--textarea style="width: 100%;" height='120' border='surround' placeholder="请输入审批建议"
+					v-model="form.reason"></u--textarea>
+			</u-form-item>
+		</u--form>
+		<view class="btnList">
+			<u-button style="width: 45%;margin-bottom: 10rpx;" :loading='loading' type="success" text="通过"
+				@click="handleAudit(1)">
+			</u-button>
+			<u-button style="width: 45%;" :loading='loading' type="error" text="驳回" @click="rejectTask(0)"></u-button>
+		</view>
+		<view class="btnConcel">
+			<u-button @click="showAction = true">更多</u-button>
+		</view>
+		<u-action-sheet :actions="actionList" :closeOnClickOverlay="true" :closeOnClickAction="true" title="更多操作" :show="showAction" @close="showAction = false" @select="selectActionClick"></u-action-sheet>
+	</view>
+</template>
+
+<script>
+	import {
+		approveTaskWithVariables,
+		rejectTask,
+		cancelTask
+	} from '@/api/wt/index.js'
+	export default {
+		name: 'taskSubmit',
+		props: {
+			businessId: {
+				default: ''
+			},
+			taskId: {
+				default: ''
+			},
+			id: {
+				default: ''
+			},
+			taskDefinitionKey: {
+				default: ''
+			}
+		},
+
+		data() {
+			return {
+				showAction: false,
+				loading: false,
+				actionList: [{
+					name: '作废',
+					fontSize: '28',
+					color: '#ffaa7f'
+				}],
+				form: {
+					technicianId: '',
+					reason: '',
+				},
+				rules: {
+					reason: {
+						type: 'string',
+						required: true,
+						message: '请输入审批建议',
+						trigger: 'blur'
+					}
+				}
+			}
+		},
+		mounted() {
+			this.$refs.uForm.setRules(this.rules)
+		},
+		methods: {
+			selectActionClick(item) {
+				console.log('selectActionClick', item)
+				if (item.name == '作废') {
+					uni.showModal({
+						title: '提示',
+						content: '是否确认作废?',
+						success: (res) => {
+							if (res.confirm) {
+								this.loading = true
+								cancelTask({
+									taskId: this.taskId,
+									id: this.id,
+									reason: this.form.reason,
+									businessId: this.businessId,
+								}).then(() => {
+									if (res.code != '-1') {
+										this.loading = false
+										this.$emit('handleAudit', {
+											title: '作废'
+										});
+									}
+								}).catch(() => {
+									this.loading = false
+									this.$message.error("流程作废失败");
+								});
+							} else if (res.cancel) {
+								console.log('用户点击取消');
+							}
+						}
+					});
+				}
+			},
+			async handleAudit(status) {
+				if (!!status) await this.$refs.uForm.validate()
+				this.loading = true
+				await this._approveTaskWithVariables(status);
+			},
+			async _approveTaskWithVariables(status) {
+				let variables = {
+					pass: !!status
+				};
+				let res = await approveTaskWithVariables({
+					id: this.taskId,
+					reason: this.form.reason,
+					variables
+				})
+
+				if (res.code != '-1') {
+					this.$emit('handleAudit', {
+						status,
+						title: status === 0 ? '驳回' : ''
+					});
+				}
+				this.loading = false
+			},
+			async rejectTask(status) {
+				let variables = {
+					pass: !!status
+				};
+				let res = await rejectTask({
+					id: this.taskId,
+					reason: this.form.reason,
+					variables
+				})
+
+				if (res.code != '-1') {
+					this.$emit('handleAudit', {
+						status,
+						title: status === 0 ? '驳回' : ''
+					});
+				}
+				this.loading = false
+			},
+			getTableValue() {
+				return new Promise((resolve, reject) => {
+					this.$emit('getTableValue', async (data) => {
+						resolve(await data);
+					});
+				});
+			}
+		}
+	}
+</script>
+
+<style scoped>
+	.btnList {
+		display: flex;
+
+	}
+	.btnConcel {
+		margin-top: 20rpx;
+	}
+</style>