فهرست منبع

物资-移动端审批功能开发

huangguoce 6 روز پیش
والد
کامیت
c7e5796ef5

+ 4 - 1
api/AppPath.js

@@ -14,4 +14,7 @@ export const PUBLIC_MODULES_PATH = "/public-modules-server";
 export const CENTRECAREFUL_PATH = "/centrecareful-server";
 export const ASSESS_PATH = "/assess-server";
 export const FINANCE_PATH = "/finance-server";
-export const LUCKY_PATH = "/lucky-server";
+export const LUCKY_PATH = "/lucky-server";
+// 
+//景聚庭进销存系统
+export const PSI_MANAGEMANT = "/psi-management-server";

+ 124 - 0
api/psi/CollectService.js

@@ -0,0 +1,124 @@
+import request from "../../common/request"
+import { PSI_MANAGEMANT as prefix } from "@/api/AppPath";
+
+export default class CollectService {
+  updateStatusById(data) {
+    return request({
+      url: prefix + "/psi/collect/updateStatusById",
+      method: "post",
+      data: data,
+    });
+  }
+
+  remove(id) {
+    return request({
+      url: prefix + "/psi/collect/remove",
+      method: "get",
+      params: { id: id },
+    });
+  }
+
+  save(data) {
+    return request({
+      url: prefix + "/psi/collect/save",
+      method: "post",
+      data: data,
+    });
+  }
+
+  findById(id) {
+    return request({
+      url: prefix + "/psi/collect/findById",
+      method: "get",
+      params: { id: id },
+    });
+  }
+
+  list(params) {
+    return request({
+      url: prefix + "/psi/collect/list",
+      method: "get",
+      params: params,
+    });
+  }
+
+  wareHouseList(params) {
+    return request({
+      url: prefix + "/psi/collect/wareHouseList",
+      method: "get",
+      params: params,
+    });
+  }
+
+  createReturnData(data) {
+    return request({
+      url: prefix + "/psi/collect/createReturnData",
+      method: "post",
+      data: data,
+    });
+  }
+
+  saveReturn(data) {
+    return request({
+      url: prefix + "/psi/collect/saveReturn",
+      method: "post",
+      data: data,
+    });
+  }
+
+  saveReturnAgree(data) {
+    return request({
+      url: prefix + "/psi/collect/saveReturnAgree",
+      method: "post",
+      data: data,
+    });
+  }
+
+  updateStatusByIdReturn(data) {
+    return request({
+      url: prefix + "/psi/collect/updateStatusByIdReturn",
+      method: "post",
+      data: data,
+    });
+  }
+
+  findByReturnId(id) {
+    return request({
+      url: prefix + "/psi/collect/findByReturnId",
+      method: "get",
+      params: { returnId: id },
+    });
+  }
+
+  returnRequest(data) {
+    return request({
+      url: prefix + "/psi/collect/returnRequest",
+      method: "post",
+      data: data,
+    });
+  }
+
+  findHiById(id) {
+    return request({
+      url: prefix + "/psi/collect/findHiById",
+      method: "get",
+      params: { collectId: id },
+    });
+  }
+
+  findHiByHiId(id) {
+    return request({
+      url: prefix + "/psi/collect/findHiByHiId",
+      method: "get",
+      params: { id: id },
+    });
+  }
+
+  findLastHiByid(id) {
+    return request({
+      url: prefix + "/psi/collect/findLastHiByid",
+      method: "get",
+      params: { collectId: id },
+    });
+  }
+}

+ 52 - 0
api/psi/LossService.js

@@ -0,0 +1,52 @@
+import request from "../../common/request"
+import { PSI_MANAGEMANT as prefix } from "@/api/AppPath";
+
+export default class LossService {
+  list(params) {
+    return request({
+      url: prefix + "/psi/loss/list",
+      method: "get",
+      params: params,
+    });
+  }
+
+  save(data) {
+    return request({
+      url: prefix + "/psi/loss/save",
+      method: "post",
+      data: data,
+    });
+  }
+
+  findById(id) {
+    return request({
+      url: prefix + "/psi/loss/findById",
+      method: "get",
+      params: { id: id },
+    });
+  }
+
+  updateStatusById(data) {
+    return request({
+      url: prefix + "/psi/loss/updateStatusById",
+      method: "post",
+      data: data,
+    });
+  }
+
+  remove(id) {
+    return request({
+      url: prefix + "/psi/loss/remove",
+      method: "get",
+      params: { id: id },
+    });
+  }
+
+  getLossByTradeName(params) {
+    return request({
+      url: prefix + "/psi/loss/getLossByTradeName",
+      method: "get",
+      params: params,
+    });
+  }
+}

+ 61 - 0
api/psi/MaterialManagementService.js

@@ -0,0 +1,61 @@
+import request from "../../common/request"
+import { PSI_MANAGEMANT as prefix } from "../AppPath";
+
+export default class MaterialManagementService {
+  list(param) {
+    return request({
+      url: prefix + '/psi/management/list',
+      method: 'get',
+      params: param
+    })
+  }
+
+  save(param) {
+    return request({
+      url: prefix + '/psi/management/save',
+      method: 'post',
+      data: param
+    })
+  }
+
+  findById(id) {
+    return request({
+      url: prefix + '/psi/management/findById',
+      method: 'get',
+      params: { id: id }
+    })
+  }
+
+  remove(id) {
+    return request({
+      url: prefix + '/psi/management/remove',
+      method: 'get',
+      params: { id: id }
+    })
+  }
+
+  updateStatusById(param) {
+    return request({
+      url: prefix + '/psi/management/updateStatusById',
+      method: 'post',
+      data: param
+    })
+  }
+
+  exportFile(params) {
+    return request({
+      url: prefix + '/psi/management/exportFile',
+      method: 'get',
+      params: params,
+      responseType: 'blob'
+    })
+  }
+
+  findTradeByTypeId(typeId) {
+    return request({
+      url: prefix + '/psi/management/findTradeByTypeId',
+      method: 'get',
+      params: { typeId: typeId }
+    })
+  }
+}

+ 52 - 0
api/psi/MaterialTypeService.js

@@ -0,0 +1,52 @@
+import request from "../../common/request"
+import { PSI_MANAGEMANT as prefix } from "@/api/AppPath";
+
+export default class MaterialTypeService {
+  list(param) {
+    return request({
+      url: prefix + '/psi/materialType/list',
+      method: 'get',
+      params: param
+    })
+  }
+
+  bxList(param) {
+    return request({
+      url: prefix + '/psi/materialType/bxList',
+      method: 'get',
+      params: param
+    })
+  }
+
+  cgList(param) {
+    return request({
+      url: prefix + '/psi/materialType/cgList',
+      method: 'get',
+      params: param
+    })
+  }
+
+  save(param) {
+    return request({
+      url: prefix + '/psi/materialType/save',
+      method: 'post',
+      data: param
+    })
+  }
+
+  findById(id) {
+    return request({
+      url: prefix + '/psi/materialType/findById',
+      method: 'get',
+      params: { id: id }
+    })
+  }
+
+  remove(id) {
+    return request({
+      url: prefix + '/psi/materialType/deleteById',
+      method: 'get',
+      params: { id: id }
+    })
+  }
+}

+ 44 - 0
api/psi/SupplierService.js

@@ -0,0 +1,44 @@
+import request from "../../common/request"
+import { PSI_MANAGEMANT as prefix } from "../AppPath";
+
+export default class SupplierService {
+  save(param) {
+    return request({
+      url: prefix + '/psi/supplier/save',
+      method: 'post',
+      data: param
+    })
+  }
+
+  queryById(id) {
+    return request({
+      url: prefix + '/psi/supplier/queryById',
+      method: 'get',
+      params: { id: id }
+    })
+  }
+
+  list(param) {
+    return request({
+      url: prefix + '/psi/supplier/list',
+      method: 'get',
+      params: param
+    })
+  }
+
+  delete(ids) {
+    return request({
+      url: prefix + '/psi/supplier/delete',
+      method: 'delete',
+      params: { ids: ids }
+    })
+  }
+
+  findByName(name) {
+    return request({
+      url: prefix + '/psi/supplier/findByName',
+      method: 'get',
+      params: { name: name }
+    })
+  }
+}

+ 214 - 0
api/psi/WareHouseService.js

@@ -0,0 +1,214 @@
+import request from "../../common/request"
+import { PSI_MANAGEMANT as prefix } from "@/api/AppPath";
+
+export default class WareHouseService {
+  list(params) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/list",
+      method: "get",
+      params: params,
+    });
+  }
+
+  reimbursementList(params) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/reimbursementList",
+      method: "get",
+      params: params,
+    });
+  }
+
+  wareHouseList(param) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/wareHouseList",
+      method: "get",
+      params: param,
+    });
+  }
+
+  exportFile(params) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/exportFile",
+      method: "get",
+      params: params,
+      responseType: "blob",
+    });
+  }
+
+  exportSummaryFile(params) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/exportSummaryFile",
+      method: "get",
+      params: params,
+      responseType: "blob",
+    });
+  }
+
+  wareHouseHistoryList(param) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/wareHouseHistoryList",
+      method: "get",
+      params: param,
+    });
+  }
+
+  collectHistoryList(param) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/collectHistoryList",
+      method: "get",
+      params: param,
+    });
+  }
+
+  wareHouseSummaryList(param) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/wareHouseSummaryList",
+      method: "get",
+      params: param,
+    });
+  }
+
+  wareHouseSummaryList2(param) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/wareHouseSummaryList2",
+      method: "get",
+      params: param,
+    });
+  }
+
+  getByProduceDateAndNameGroup(param) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/getByProduceDateAndNameGroup",
+      method: "get",
+      params: param,
+    });
+  }
+
+  getByProduceDateNotMerge(param) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/getByProduceDateNotMerge",
+      method: "get",
+      params: param,
+    });
+  }
+
+  getAllData(param) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/getAllData",
+      method: "get",
+      params: param,
+    });
+  }
+
+  save(param) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/save",
+      method: "post",
+      data: param,
+    });
+  }
+
+  findById(id, status) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/findById",
+      method: "get",
+      params: { id: id, status: status },
+    });
+  }
+
+  remove(id) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/remove",
+      method: "get",
+      params: { id: id },
+    });
+  }
+
+  findRequestId(purchaseNo) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/findRequestId",
+      method: "get",
+      params: { purchaseNo: purchaseNo },
+    });
+  }
+
+  updateStatusById(data) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/updateStatusById",
+      method: "post",
+      data: data,
+    });
+  }
+
+  backSourceData(id) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/backSourceData",
+      method: "get",
+      params: { id: id },
+    });
+  }
+
+  findHiById(id) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/findHiById",
+      method: "get",
+      params: { id: id },
+    });
+  }
+
+  findHiByHiId(id) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/findHiByHiId",
+      method: "get",
+      params: { id: id },
+    });
+  }
+
+  findLastHiByid(id) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/findLastHiByid",
+      method: "get",
+      params: { id: id },
+    });
+  }
+
+  updateWarnFlagByTradeNameAndType(tradeName, wareHouseType) {
+    return request({
+      url: prefix + `/psi/management/wareHouse/updateWarnFlagByTradeNameAndType`,
+      method: "post",
+      data: { tradeName: tradeName, wareHouseType: wareHouseType },
+    });
+  }
+
+  updateWarnNumByTradeNameAndType(tradeName, wareHouseType, warnNum) {
+    return request({
+      url: prefix + `/psi/management/wareHouse/updateWarnNumByTradeNameAndType`,
+      method: "post",
+      data: {
+        tradeName: tradeName,
+        wareHouseType: wareHouseType,
+        warnNum: warnNum,
+      },
+    });
+  }
+
+  saveTradeName(oldTradeName, tradeName, type) {
+    return request({
+      url: prefix + `/psi/management/wareHouse/saveTradeName`,
+      method: "post",
+      data: {
+        oldTradeName: oldTradeName,
+        tradeName: tradeName,
+        wareHouseType: type,
+      },
+    });
+  }
+
+  findTradeByTypeId(typeId) {
+    return request({
+      url: prefix + "/psi/management/wareHouse/findTradeByTypeId",
+      method: "get",
+      params: { typeId: typeId },
+    });
+  }
+}

+ 167 - 149
pages/addressbook/addressbook.vue

@@ -1,175 +1,193 @@
 <template>
-<view>
-	<cu-custom bgColor="bg-blue">
-		<block slot="content">通讯录</block>
-	</cu-custom>
-
-	<u-sticky>
-		<view  style="padding: 20upx; background-color: white;">
-			<u-search placeholder="姓名" :clearabled="true" :showAction="false" v-model="searchUserName"></u-search>
-		</view>
-		<view  style="padding: 20upx; background-color: white;">
-			<u-search placeholder="部门" :clearabled="true" :showAction="false" v-model="officeName"></u-search>
-		</view>
-		<view  style="padding: 20upx; background-color: white;">
-			<u-search placeholder="手机号" :clearabled="true" :showAction="false" v-model="mobile"></u-search>
-		</view>
-	</u-sticky>
-	
 	<view>
-		<u-index-list :indexList="indexList">
-			<template v-for="(item, index) in list">
-				<!-- #ifdef APP-NVUE -->
-				<u-index-anchor :text="list[index].letter" :key="index"></u-index-anchor>
-				<!-- #endif -->
-				<u-index-item :key="index">
-					<!-- #ifndef APP-NVUE -->
-					<u-index-anchor :text="list[index].letter"></u-index-anchor>
+		<cu-custom bgColor="bg-blue">
+			<block slot="content">通讯录</block>
+		</cu-custom>
+
+		<u-sticky>
+			<view style="padding: 20upx; background-color: white;">
+				<u-search placeholder="姓名" :clearabled="true" :showAction="false" v-model="searchUserName"></u-search>
+			</view>
+			<view style="padding: 20upx; background-color: white;">
+				<u-search placeholder="部门" :clearabled="true" :showAction="false" v-model="officeName"></u-search>
+			</view>
+			<view style="padding: 20upx; background-color: white;">
+				<u-search placeholder="手机号" :clearabled="true" :showAction="false" v-model="mobile"></u-search>
+			</view>
+		</u-sticky>
+
+		<view>
+			<u-index-list :indexList="indexList">
+				<template v-for="(item, index) in list">
+					<!-- #ifdef APP-NVUE -->
+					<u-index-anchor :text="list[index].letter" :key="index"></u-index-anchor>
 					<!-- #endif -->
-					<view class="list" v-for="(user, index1) in list[index].data" :key="index1">
-						<!-- 检查user.name是否不等于"管理员" -->
-						<view class="list__item" v-if="user.name !== '管理员'">
-							<u-avatar shape="square" size="35" icon="account-fill" fontSize="26" randomBgColor></u-avatar>
-							<!-- <view class="cu-avatar round " :style="'background-image:url('+(user.photo?user.photo:'/h5/static/user/flat-avatar.png')+');'"></view> -->
-							<text class="list__item__user-name">{{user.name}}</text>
-							<!-- 新增的展示其他信息的元素 -->
-							<!-- 使用条件渲染检查 user.officeDTO.name 是否为 null -->
-							<text class="list__item__additional-info" v-if="user.officeDTO">{{ user.officeDTO.name }}</text>
-							<!-- 新的电话号码信息 -->
-							<text class="list__item__phone-number">{{ user.mobile }}</text>
+					<u-index-item :key="index">
+						<!-- #ifndef APP-NVUE -->
+						<u-index-anchor :text="list[index].letter"></u-index-anchor>
+						<!-- #endif -->
+						<view class="list" v-for="(user, index1) in list[index].data" :key="index1">
+							<!-- 检查user.name是否不等于"管理员" -->
+							<view class="list__item" v-if="user.name !== '管理员'">
+								<u-avatar shape="square" size="35" icon="account-fill" fontSize="26"
+									randomBgColor></u-avatar>
+								<!-- <view class="cu-avatar round " :style="'background-image:url('+(user.photo?user.photo:'/h5/static/user/flat-avatar.png')+');'"></view> -->
+								<text class="list__item__user-name">{{ user.name }}</text>
+								<!-- 新增的展示其他信息的元素 -->
+								<!-- 使用条件渲染检查 user.officeDTO.name 是否为 null -->
+								<text class="list__item__additional-info" v-if="user.officeDTO">{{ user.officeDTO.name
+								}}</text>
+								<!-- 新的电话号码信息 -->
+								<text class="list__item__phone-number">{{ user.mobile }}</text>
+							</view>
+							<u-line v-if="user.name !== '管理员'"></u-line>
 						</view>
-						<u-line v-if="user.name !== '管理员'"></u-line>
-					</view>
-				</u-index-item>
-			</template>
-			<view style="font-size: 15px; color: gray; text-align: center; margin-top: 15px;">共{{total}}位好友</view>
-		</u-index-list>
-		<u-gap height="100" bgColor="#fff"></u-gap>
+					</u-index-item>
+				</template>
+				<view style="font-size: 15px; color: gray; text-align: center; margin-top: 15px;">共{{ total }}位好友</view>
+			</u-index-list>
+			<u-gap height="100" bgColor="#fff"></u-gap>
+		</view>
 	</view>
-</view>	
 </template>
 
 <script>
-	import userService from '@/api/sys/userService'
-	export default {
-		data() {
-			return {
-				indexList: [],
-				userList: [],
-				total: 0,
-				searchUserName: '',
-				officeName: '',
-				mobile:''
-			}
+import userService from '@/api/sys/userService'
+export default {
+	data() {
+		return {
+			indexList: [],
+			userList: [],
+			total: 0,
+			searchUserName: '',
+			officeName: '',
+			mobile: ''
+		}
+	},
+	created() {
+		let params = { current: 1, size: 10000, tenantDTO: {} }
+		if (this.userInfo.tenantDTO.id == "10009") {
+			params.tenantDTO.id = this.userInfo.tenantDTO.id
+		}
+
+		userService.list(params).then((data) => {
+			this.userList = data.records
+			this.total = data.total
+		}).catch((e) => {
+			throw e
+		})
+	},
+	computed: {
+		userInfo() {
+			return this.$store.state.user.userInfo
 		},
-		created() {
-			userService.list({current: 1, size: 10000}).then((data)=>{
-				this.userList = data.records
-				this.total = data.total
-			}).catch((e)=>{
-				throw e
+		list() {
+			let resultList = this.userList.filter((item) => {
+				if (item.name.indexOf(this.searchUserName) >= 0) {
+					return true
+				}
 			})
-		},
-		computed: {
-			list () {
-				let resultList = this.userList.filter((item)=>{
-					if(item.name.indexOf(this.searchUserName) >= 0){
+			resultList = resultList.filter((item) => {
+				if (item.officeDTO && item.officeDTO.name) {
+					if (item.officeDTO.name.indexOf(this.officeName) >= 0) {
 						return true
 					}
-				})
-				resultList = resultList.filter((item)=>{
-					if(item.officeDTO && item.officeDTO.name){
-						if(item.officeDTO.name.indexOf(this.officeName) >= 0){
-							return true
-						}
+				}
+			})
+			resultList = resultList.filter((item) => {
+				if (item.mobile) {
+					if (item.mobile.indexOf(this.mobile) >= 0) {
+						return true
 					}
-				})
-				resultList = resultList.filter((item)=>{
-					if(item.mobile){
-						if(item.mobile.indexOf(this.mobile) >= 0){
-							return true
-						}
+				}
+			})
+			this.total = resultList.length
+			return this.pySegSort(resultList)
+		}
+	},
+	methods: {
+		// 排序
+		pySegSort(arr) {
+			if (!String.prototype.localeCompare)
+				return null;
+
+			var letters = "0abcdefghjklmnopqrstwxyz".split('');
+			var zh = "阿八嚓哒妸发旮哈讥咔垃痳拏噢妑七呥扨它穵夕丫帀".split('');
+
+			var segs = [];
+			this.indexList = [];
+			var curr;
+			letters.forEach((item, i) => {
+				curr = { letter: item, data: [] };
+				arr.forEach((item2) => {
+					if ((!zh[i - 1] || zh[i - 1].localeCompare(item2.name) <= 0) && item2.name.localeCompare(zh[i]) == -1) {
+						curr.data.push(item2);
 					}
-				})
-				this.total = resultList.length
-				return this.pySegSort(resultList)
-			}
-		},
-		methods: {
-			// 排序
-			pySegSort(arr) {
-			    if(!String.prototype.localeCompare)
-			        return null;
-			     
-			    var letters = "0abcdefghjklmnopqrstwxyz".split('');
-			    var zh = "阿八嚓哒妸发旮哈讥咔垃痳拏噢妑七呥扨它穵夕丫帀".split('');
-			     
-			    var segs = [];
-				this.indexList = [];
-			    var curr;
-			    letters.forEach((item,i) => {
-			        curr = {letter: item, data:[]};
-			        arr.forEach((item2) => {
-			            if((!zh[i-1] || zh[i-1].localeCompare(item2.name) <= 0) && item2.name.localeCompare(zh[i]) == -1) {
-			                curr.data.push(item2);
-			            }
-			        });
-			        if(curr.data.length) {
-			            segs.push(curr);
-						this.indexList.push(curr.letter)
-			            curr.data.sort((a,b)=>{
-			                return a.name.localeCompare(b.name);
-			            });
-			        }
-			    });
-			    return segs;
-			}
+				});
+				if (curr.data.length) {
+					segs.push(curr);
+					this.indexList.push(curr.letter)
+					curr.data.sort((a, b) => {
+						return a.name.localeCompare(b.name);
+					});
+				}
+			});
+			return segs;
 		}
 	}
+}
 </script>
 
 <style lang="scss">
-	.list {
-		
-		&__item {
-			@include flex;
-			padding: 6px 12px;
-			align-items: center;
-			
-			&__avatar {
-				height: 35px;
-				width: 35px;
-				border-radius: 3px;
-			}
-			
-			&__user-name {
-				font-size: 16px;
-				margin-left: 10px;
-				color: $u-main-color;
-			}
+.list {
+
+	&__item {
+		@include flex;
+		padding: 6px 12px;
+		align-items: center;
+
+		&__avatar {
+			height: 35px;
+			width: 35px;
+			border-radius: 3px;
 		}
-		
-		&__footer {
-			color: $u-tips-color;
-			font-size: 14px;
-			text-align: center;
-			margin: 15px 0;
+
+		&__user-name {
+			font-size: 16px;
+			margin-left: 10px;
+			color: $u-main-color;
 		}
 	}
 
-	.list__item__additional-info {
-		position: relative;
-		top: 10px; /* Adjust as needed */
-		left: 15px; /* Adjust as needed */
-		font-size: 12px; /* Adjust the font size as needed */
-		color: #999; /* Adjust the color as needed */
+	&__footer {
+		color: $u-tips-color;
+		font-size: 14px;
+		text-align: center;
+		margin: 15px 0;
 	}
+}
 
-	.list__item__phone-number {
-		position: relative;
-		top: 10px; /* Adjust as needed */
-		margin-left: 20px; /* 根据需要调整间距 */
-		font-size: 12px; /* 根据需要调整字体大小 */
-		color: #999; /* 根据需要调整颜色 */
-	}
+.list__item__additional-info {
+	position: relative;
+	top: 10px;
+	/* Adjust as needed */
+	left: 15px;
+	/* Adjust as needed */
+	font-size: 12px;
+	/* Adjust the font size as needed */
+	color: #999;
+	/* Adjust the color as needed */
+}
+
+.list__item__phone-number {
+	position: relative;
+	top: 10px;
+	/* Adjust as needed */
+	margin-left: 20px;
+	/* 根据需要调整间距 */
+	font-size: 12px;
+	/* 根据需要调整字体大小 */
+	color: #999;
+	/* 根据需要调整颜色 */
+}
 </style>

+ 839 - 0
pages/psiManagement/collect/CollectForm.vue

@@ -0,0 +1,839 @@
+<template>
+    <view>
+        <cu-custom :backUrl="'/pages/index/index'" :isBack="true" bgColor="bg-gradual-blue">
+            <block slot="content">领用申请</block>
+        </cu-custom>
+
+        <u--form :model="inputForm" labelWidth="100px" class="u-form" labelPosition="left" :rules="rules"
+            ref="inputForm">
+            <u-form-item label="领用编号" borderBottom prop="collectNo">
+                <u--input placeholder="自动生成" v-model="inputForm.collectNo" disabled></u--input>
+            </u-form-item>
+            <u-form-item label="经办人" borderBottom prop="handledBy">
+                <u--input v-model="inputForm.handledBy" disabled></u--input>
+            </u-form-item>
+            <u-form-item label="经办部门" borderBottom prop="handledByOfficeName">
+                <u--input v-model="inputForm.handledByOfficeName" disabled></u--input>
+            </u-form-item>
+            <u-form-item label="领用时间" borderBottom prop="collectDate" :required="true">
+                <el-date-picker v-model="inputForm.collectDate" type="date" placeholder="请选择领用时间" style="width:100%"
+                    placement="bottom-start" clearable :disabled="nodeFlag">
+                </el-date-picker>
+            </u-form-item>
+            <u-form-item label="备注" borderBottom prop="remarks">
+                <u--textarea placeholder="请填写备注信息" :rows="4" :maxlength="500" v-model="inputForm.remarks"
+                    :disabled="nodeFlag"></u--textarea>
+            </u-form-item>
+
+            <view class="section-wrap">
+                <view class="section-title">领用详情</view>
+                <view class="section-tip"></view>
+
+                <view v-for="(item, index) in inputForm.detailInfos" :key="item.id || index" class="detail-card">
+                    <view class="detail-card-title">领用详情 {{ index + 1 }}</view>
+
+                    <u-form-item label="领用人" :prop="'detailInfos[' + index + '].recipientAgent'" :required="true">
+                        <u--input v-model="inputForm.detailInfos[index].recipientAgent" placeholder="请选择领用人"
+                            :disabled="nodeFlag" @focus="openUserPullForm(index)" clearable></u--input>
+                    </u-form-item>
+                    <u-form-item label="领用人部门" :prop="'detailInfos[' + index + '].recipientOffice'">
+                        <u--input v-model="inputForm.detailInfos[index].recipientOffice" disabled></u--input>
+                    </u-form-item>
+                    <u-form-item label="领用类型" :prop="'detailInfos[' + index + '].collectType'" :required="true">
+                        <u--input v-model="inputForm.detailInfos[index].collectType" placeholder="请选择领用类型"
+                            :disabled="nodeFlag" @focus="openTypePicker(index)" clearable></u--input>
+                    </u-form-item>
+                    <!-- <u-form-item label="物品名称" :prop="'detailInfos[' + index + '].goodsName'" :required="true"> -->
+                    <!-- <u--input :value="inputForm.detailInfos[index].goodsName" placeholder="请选择物品名称"
+                            :disabled="nodeFlag" @focus="openPicker(index)"></u--input> -->
+                    <!-- </u-form-item> -->
+                    <collect-goods-selector ref="goodsSelect" :rowIndex="index" :inputForm="inputForm"
+                        :disabled="nodeFlag" @selected="handleGoodsSelected"></collect-goods-selector>
+                    <u-form-item label="领用数量" :prop="'detailInfos[' + index + '].collectNumber'" :required="true">
+                        <u--input v-model="inputForm.detailInfos[index].collectNumber" placeholder="请输入领用数量"
+                            :disabled="nodeFlag" @blur="handleCollectNumberBlur(index)"></u--input>
+                    </u-form-item>
+                    <u-form-item label="库存数量" :prop="'detailInfos[' + index + '].kc'">
+                        <u--input v-model="inputForm.detailInfos[index].kc" disabled></u--input>
+                    </u-form-item>
+                    <u-form-item label="单位" :prop="'detailInfos[' + index + '].company'">
+                        <u--input v-model="inputForm.detailInfos[index].company" placeholder="请输入单位"
+                            :disabled="true"></u--input>
+                    </u-form-item>
+                    <u-form-item label="包装规格" :prop="'detailInfos[' + index + '].spec'">
+                        <u--input v-model="inputForm.detailInfos[index].spec" disabled></u--input>
+                    </u-form-item>
+
+
+                    <u-form-item label="备注" :prop="'detailInfos[' + index + '].remarks'">
+                        <u--input v-model="inputForm.detailInfos[index].remarks" placeholder="请输入备注"
+                            :disabled="nodeFlag"></u--input>
+                    </u-form-item>
+                    <u-form-item label="附件上传">
+                        <UploadComponent :uploadUrl="`${uploadUrl}/public-modules-server/oss/file/webUpload/upload`"
+                            @onRemove="(file, fileList, fileIndex) => handleRemove(file, fileList, index, fileIndex, 'detail')"
+                            @onSuccess="(file, fileList) => handleUploadSuccess(file, fileList, index, 'detail')"
+                            :fileList="inputForm.detailInfos[index].fileInfoLost" :limit="3" :isDelete="nodeFlag"
+                            :isUpload="nodeFlag">
+                        </UploadComponent>
+                    </u-form-item>
+                    <u-form-item label="" v-if="!nodeFlag">
+                        <el-button style="width: 100%" type="danger" plain @click="removeDetail(index)">
+                            删除领用详情
+                        </el-button>
+                    </u-form-item>
+                </view>
+
+                <u-form-item label="" v-if="!nodeFlag">
+                    <el-button style="width: 100%" type="primary" @click="addDetail()" plain>新增领用详情</el-button>
+                </u-form-item>
+            </view>
+
+            <view class="section-wrap" v-if="nodeFlag">
+                <view class="section-title">领用分配详情</view>
+
+                <view v-for="(item, index) in inputForm.recordList" :key="item.id || index" class="detail-card">
+                    <view class="detail-card-title">领用分配详情 {{ index + 1 }}</view>
+
+                    <u-form-item label="领用人" :prop="'recordList[' + index + '].recipientAgent'" :required="true">
+                        <u--input v-model="inputForm.recordList[index].recipientAgent" placeholder="请选择领用人"
+                            :disabled="nodeFlag" @focus="openUserPullForm(index)" clearable></u--input>
+                    </u-form-item>
+                    <u-form-item label="领用人部门" :prop="'recordList[' + index + '].recipientOffice'">
+                        <u--input v-model="inputForm.recordList[index].recipientOffice" disabled></u--input>
+                    </u-form-item>
+                    <u-form-item label="领用类型" :prop="'recordList[' + index + '].collectType'" :required="true">
+                        <u--input v-model="inputForm.recordList[index].collectType" placeholder="请选择领用类型"
+                            :disabled="nodeFlag" @focus="openTypePicker(index)" clearable></u--input>
+                    </u-form-item>
+                    <u-form-item label="物品名称" :prop="'recordList[' + index + '].goodsName'" :required="true">
+                        <u--input v-model="inputForm.recordList[index].goodsName" placeholder="请选择物品名称"
+                            :disabled="nodeFlag" clearable></u--input>
+                    </u-form-item>
+                    <u-form-item label="分配生产日期" :prop="'recordList[' + index + '].fpDate'" :required="true">
+                        <u--input v-model="inputForm.recordList[index].fpDate" placeholder="请选择分配生产日期"
+                            :disabled="nodeFlag" clearable></u--input>
+                    </u-form-item>
+                    <u-form-item label="领用数量" :prop="'recordList[' + index + '].collectNumber'" :required="true">
+                        <u--input v-model="inputForm.recordList[index].collectNumber" placeholder="请输入领用数量"
+                            :disabled="nodeFlag" @blur="handleCollectNumberBlur(index)"></u--input>
+                    </u-form-item>
+                    <u-form-item label="库存数量" :prop="'recordList[' + index + '].kc'">
+                        <u--input v-model="inputForm.recordList[index].kc" disabled></u--input>
+                    </u-form-item>
+                </view>
+            </view>
+        </u--form>
+
+        <user-select ref="userPicker" @input="handleUserSelected"></user-select>
+        <ba-tree-picker ref="treePicker" :multiple="false" @select-change="selectTypeChange" title="类型选择"
+            :localdata="listData" :selectedData="typeSelectedData" valueKey="value" textKey="label"
+            childrenKey="children" />
+    </view>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+import userSelect from '@/components/user-select/user-select-radio.vue'
+import baTreePicker from '@/components/ba-tree-picker/ba-tree-picker.vue'
+import UploadComponent from '@/pages/common/UploadComponent.vue'
+import upload from '@/utils/upload.js'
+import MaterialTypeService from '@/api/psi/MaterialTypeService'
+import WareHouseService from '@/api/psi/WareHouseService'
+import CollectService from '@/api/psi/CollectService'
+import CommonApi from '@/api/common/CommonApi'
+import CollectGoodsSelector from './CollectGoodsSelector.vue'
+
+export default {
+    name: 'PsiCollectForm',
+    components: {
+        userSelect,
+        baTreePicker,
+        UploadComponent,
+        CollectGoodsSelector
+    },
+    computed: mapState({
+        userInfo: (state) => state.user.userInfo
+    }),
+    props: {
+        businessId: {
+            type: String,
+            default: ''
+        },
+        formReadOnly: {
+            type: Boolean,
+            default: false
+        },
+        status: {
+            type: String,
+            default: ''
+        }
+    },
+    data() {
+        return {
+            uploadUrl: upload.UPLOAD_URL,
+            nodeFlag: false,
+            loading: false,
+            listData: [],
+            materialList: [],
+            typeSelectedData: [],
+            inputForm: this.createInputForm(),
+            rules: {
+                collectDate: [
+                    {
+                        required: true,
+                        message: '领用时间不能为空',
+                        trigger: ['change']
+                    }
+                ]
+            }
+        }
+    },
+    materialTypeService: null,
+    wareHouseService: null,
+    collectService: null,
+    commonApi: null,
+    created() {
+        this.ensureServices()
+        this.loadMaterialTypes()
+        this.fillUserInfo()
+    },
+    watch: {
+        businessId: {
+            handler() {
+                if (this.businessId) {
+                    this.init(this.businessId)
+                } else {
+                    this.resetForm()
+                    this.nodeFlag = this.formReadOnly || this.status === 'taskFormDetail' || this.status === 'testSee'
+                }
+            },
+            immediate: true,
+            deep: false
+        }
+    },
+    methods: {
+        createInputForm() {
+            return {
+                id: '',
+                procInsId: '',
+                processDefinitionId: '',
+                status: '',
+                collectNo: '',
+                collectDate: '',
+                remarks: '',
+                handledBy: '',
+                handledById: '',
+                handledByOffice: '',
+                handledByOfficeName: '',
+                userId: '',
+                files: [],
+                detailInfos: [],
+                recordList: [],
+                returnId: '',
+                statusReturn: '',
+                procInsIdReturn: '',
+                processDefinitionIdReturn: '',
+                returnCause: '',
+                returnFiles: []
+            }
+        },
+        createDetailRow() {
+            return {
+                id: '',
+                recipientAgent: (this.userInfo && this.userInfo.name) || '',
+                recipientAgentId: (this.userInfo && this.userInfo.id) || '',
+                recipientOffice: (this.userInfo && this.userInfo.officeDTO && this.userInfo.officeDTO.name) || '',
+                collectType: '',
+                collectTypeId: '',
+                goodsName: '',
+                surplusNumber: '',
+                currentInventory: '',
+                collectNumber: '',
+                company: '',
+                remarks: '',
+                spec: '',
+                produceDate: '',
+                shelfLife: '',
+                shelfLifeUnit: '',
+                fileInfoLost: [],
+                isReturn: '0'
+            }
+        },
+        ensureServices() {
+            if (!this.materialTypeService) {
+                this.materialTypeService = new MaterialTypeService()
+            }
+            if (!this.wareHouseService) {
+                this.wareHouseService = new WareHouseService()
+            }
+            if (!this.collectService) {
+                this.collectService = new CollectService()
+            }
+            if (!this.commonApi) {
+                this.commonApi = new CommonApi()
+            }
+        },
+        async loadMaterialTypes() {
+            try {
+                const data = await this.materialTypeService.cgList()
+                this.materialList = data || []
+                this.listData = this.buildTree(this.materialList)
+            } catch (e) {
+                this.materialList = []
+                this.listData = []
+            }
+        },
+        fillUserInfo() {
+            if (!this.userInfo) {
+                return
+            }
+            this.inputForm.handledBy = this.inputForm.handledBy || this.userInfo.name
+            this.inputForm.handledById = this.inputForm.handledById || this.userInfo.id
+            this.inputForm.userId = this.inputForm.userId || this.userInfo.id
+            this.inputForm.handledByOffice = this.inputForm.handledByOffice || (this.userInfo.officeDTO && this.userInfo.officeDTO.id) || ''
+            this.inputForm.handledByOfficeName = this.inputForm.handledByOfficeName || (this.userInfo.officeDTO && this.userInfo.officeDTO.name) || ''
+        },
+        resetForm() {
+            this.inputForm = this.createInputForm()
+            this.fillUserInfo()
+        },
+        async init(id) {
+            this.ensureServices()
+            this.resetForm()
+            this.inputForm.id = id
+            this.loading = true
+            try {
+                const data = await this.collectService.findById(id)
+                this.inputForm = this.normalizeFormData(this.recover(this.createInputForm(), data))
+                this.inputForm.detailInfos.forEach(detail => {
+                    if (detail.surplusNumber && detail.spec) {
+                        detail.kc = this.formatNumber(Number(detail.surplusNumber) * Number(detail.spec))
+                    }
+                })
+                this.inputForm.recordList.forEach(record => {
+                    if (record.surplusNumber && record.spec) {
+                        record.kc = this.formatNumber(Number(record.surplusNumber) * Number(record.spec))
+                    }
+                    record.fpDate = `${record.goodsName}(生产日期:${record.produceDate})`
+                })
+                this.fillUserInfo()
+                this.nodeFlag = await this.resolveNodeFlag(data)
+            } catch (e) {
+                this.nodeFlag = true
+            } finally {
+                this.loading = false
+            }
+        },
+        async resolveNodeFlag(data) {
+            if ((this.formReadOnly || this.status === 'taskFormDetail' || this.status === 'testSee')) {
+                return true
+            }
+            try {
+                const taskName = await this.commonApi.getTaskNameByProcInsId((data && data.procInsId) || this.inputForm.procInsId)
+                if (this.isNotEmpty(taskName)) {
+                    return taskName !== '发起人重新发起申请'
+                }
+            } catch (e) {
+            }
+            return false
+        },
+        normalizeFormData(data) {
+            const form = data || this.createInputForm()
+            if (this.isNotEmpty(form.collectDate)) {
+                form.collectDate = new Date(form.collectDate)
+            }
+            form.files = form.files || []
+            form.detailInfos = (form.detailInfos || []).map(item => ({
+                ...item,
+                fileInfoLost: item.fileInfoLost || [],
+                produceDate: this.isNotEmpty(item.produceDate) ? new Date(item.produceDate) : '',
+                surplusNumber: this.isNotEmpty(item.surplusNumber) ? this.formatNumber(item.surplusNumber) : '',
+                currentInventory: this.isNotEmpty(item.currentInventory) ? this.formatNumber(item.currentInventory) : ''
+            }))
+            form.recordList = form.recordList || []
+            return form
+        },
+        buildTree(nodes, parentId = '0') {
+            const tree = []
+            for (const node of nodes || []) {
+                if (node.parentId === parentId) {
+                    const children = this.buildTree(nodes, node.id)
+                    if (children.length) {
+                        node.children = children
+                    }
+                    tree.push(node)
+                }
+            }
+            return tree
+        },
+        isEmpty(value) {
+            if (value === null || value === undefined) {
+                return true
+            }
+            if (typeof value === 'string' && value.trim() === '') {
+                return true
+            }
+            if (Array.isArray(value) && value.length === 0) {
+                return true
+            }
+            return false
+        },
+        isNotEmpty(value) {
+            return !this.isEmpty(value)
+        },
+        formatDate(date) {
+            if (this.isEmpty(date)) {
+                return ''
+            }
+            const dateValue = new Date(date)
+            if (Number.isNaN(dateValue.getTime())) {
+                return ''
+            }
+            const year = dateValue.getFullYear()
+            const month = `${dateValue.getMonth() + 1}`.padStart(2, '0')
+            const day = `${dateValue.getDate()}`.padStart(2, '0')
+            return `${year}-${month}-${day}`
+        },
+        normalizeDateFieldsBeforeValidate() {
+            if (this.isNotEmpty(this.inputForm.collectDate)) {
+                this.inputForm.collectDate = this.formatDate(this.inputForm.collectDate)
+            }
+        },
+        formatNumber(value) {
+            const numberValue = Number(value || 0)
+            if (Number.isNaN(numberValue)) {
+                return ''
+            }
+            return numberValue % 1 === 0 ? String(numberValue) : numberValue.toFixed(2).replace(/\.?0+$/, '')
+        },
+        formatNumberInput(inputValue, decimalLimit = 2) {
+            if (this.isEmpty(inputValue)) {
+                return ''
+            }
+            const valueText = String(inputValue)
+            if (!/^\d*\.?\d*$/.test(valueText)) {
+                return ''
+            }
+            let value = valueText.replace(/[^\d.]/g, '')
+            const dotIndex = value.indexOf('.')
+            if (dotIndex !== -1) {
+                const substr = value.substr(dotIndex + 1)
+                if (substr.indexOf('.') !== -1) {
+                    value = value.substr(0, dotIndex + 1) + substr.replace(/\./g, '')
+                }
+            }
+            if (dotIndex !== -1) {
+                const integerPart = value.substring(0, dotIndex)
+                const decimalPart = value.substring(dotIndex + 1, dotIndex + 1 + decimalLimit)
+                value = integerPart + '.' + decimalPart
+            }
+            return value
+        },
+        addDetail() {
+            this.inputForm.detailInfos.push(this.createDetailRow())
+        },
+        removeDetail(index) {
+            this.inputForm.detailInfos.splice(index, 1)
+        },
+        patchDetail(index, patch) {
+            const detail = (this.inputForm.detailInfos || [])[index] || {}
+            this.$set(this.inputForm.detailInfos, index, {
+                ...detail,
+                ...patch
+            })
+        },
+        openUserPullForm(index) {
+            if (this.nodeFlag) {
+                return
+            }
+            this.$refs.userPicker.open(index, 'detail')
+        },
+        handleUserSelected(data, index, type) {
+            if (type !== 'detail') {
+                return
+            }
+            this.patchDetail(index, {
+                recipientAgentId: data.id,
+                recipientAgent: data.label,
+                recipientOffice: data.parentLabel
+            })
+        },
+        openTypePicker(index) {
+            if (this.nodeFlag) {
+                return
+            }
+            const detail = (this.inputForm.detailInfos || [])[index] || {}
+            this.typeSelectedData = detail.collectTypeId ? [detail.collectTypeId] : []
+            this.$nextTick(() => {
+                const treePicker = this.$refs.treePicker
+                if (!treePicker) {
+                    return
+                }
+                if (treePicker._initTree) {
+                    treePicker._initTree()
+                }
+                treePicker._show(index)
+            })
+        },
+        selectTypeChange(ids, names, index) {
+            this.patchDetail(index, {
+                collectType: names,
+                collectTypeId: ids[0],
+                goodsName: '',
+                surplusNumber: '',
+                currentInventory: '',
+                collectNumber: '',
+                company: '',
+                kc: '',
+                spec: '',
+                produceDate: '',
+                shelfLife: '',
+                shelfLifeUnit: ''
+            })
+        },
+        async handleGoodsSelected({ index, item }) {
+            this.patchDetail(index, {
+                goodsName: item.tradeName || '',
+                kc: this.formatNumber(Number(item.tradeNumber) * Number(item.spec))
+            })
+            await this.syncDetailStock(index)
+        },
+        async syncDetailStock(index) {
+            const detail = (this.inputForm.detailInfos || [])[index]
+            if (!detail || this.isEmpty(detail.goodsName) || this.isEmpty(detail.collectTypeId)) {
+                return
+            }
+            try {
+                const data = await this.wareHouseService.getByProduceDateNotMerge({
+                    current: 1,
+                    size: 1000,
+                    tradeName: detail.goodsName,
+                    wareHouseType: detail.collectTypeId
+                })
+                const records = (data && data.records) || []
+                const totalBottle = records.reduce((sum, item) => {
+                    const currentInventory = Number(item.currentInventory || 0)
+                    const spec = Number(item.spec || 1)
+                    return sum + currentInventory * (spec > 0 ? spec : 1)
+                }, 0)
+                const firstRecord = records[0] || {}
+                this.patchDetail(index, {
+                    surplusNumber: this.formatNumber(totalBottle),
+                    currentInventory: this.formatNumber(totalBottle),
+                    company: firstRecord.company || detail.company || '',
+                    spec: firstRecord.spec || '',
+                    produceDate: this.isNotEmpty(firstRecord.produceDate) ? new Date(firstRecord.produceDate) : '',
+                    shelfLife: firstRecord.shelfLife || '',
+                    shelfLifeUnit: firstRecord.shelfLifeUnit || ''
+                })
+            } catch (e) {
+                this.patchDetail(index, {
+                    surplusNumber: '',
+                    currentInventory: ''
+                })
+                uni.showToast({
+                    title: '库存数量加载失败',
+                    icon: 'none'
+                })
+            }
+        },
+        handleCollectNumberBlur(index) {
+            const detail = (this.inputForm.detailInfos || [])[index]
+            if (!detail) {
+                return
+            }
+            detail.collectNumber = this.formatNumberInput(detail.collectNumber)
+            if (this.isNotEmpty(detail.surplusNumber) && this.isNotEmpty(detail.collectNumber) &&
+                Number(detail.collectNumber) > Number(detail.surplusNumber)) {
+                uni.showToast({
+                    title: '领用数量不能大于库存数量',
+                    icon: 'none'
+                })
+                detail.collectNumber = ''
+            }
+        },
+        handleUploadSuccess(file, fileList, index, type) {
+            if (type === 'detail') {
+                this.inputForm.detailInfos[index].fileInfoLost = fileList
+            } else {
+                this.inputForm.files = fileList
+            }
+        },
+        handleRemove(file, fileList, lineIndex, fileIndex, type) {
+            if (type === 'detail') {
+                this.inputForm.detailInfos[lineIndex].fileInfoLost.splice(fileIndex, 1)
+            } else {
+                this.inputForm.files.splice(fileIndex, 1)
+            }
+        },
+        validateDetailInfos() {
+            if (this.isEmpty(this.inputForm.detailInfos)) {
+                this.$message.error('至少填写一条领用详情信息')
+                return false
+            }
+            for (let i = 0; i < this.inputForm.detailInfos.length; i++) {
+                const detail = this.inputForm.detailInfos[i]
+                const lineNo = i + 1
+                if (this.isEmpty(detail.recipientAgentId)) {
+                    uni.showToast({
+                        title: `领用详情第${lineNo}行请选择领用人`,
+                        icon: 'none'
+                    })
+                    return false
+                }
+                if (this.isEmpty(detail.collectTypeId)) {
+                    uni.showToast({
+                        title: `领用详情第${lineNo}行请选择领用类型`,
+                        icon: 'none'
+                    })
+                    return false
+                }
+                if (this.isEmpty(detail.goodsName)) {
+                    uni.showToast({
+                        title: `领用详情第${lineNo}行请选择物品名称`,
+                        icon: 'none'
+                    })
+                    return false
+                }
+                if (this.isEmpty(detail.collectNumber)) {
+                    uni.showToast({
+                        title: `领用详情第${lineNo}行请输入领用数量`,
+                        icon: 'none'
+                    })
+                    return false
+                }
+                if (this.isNotEmpty(detail.surplusNumber) && Number(detail.collectNumber) > Number(detail.surplusNumber)) {
+                    uni.showToast({
+                        title: `领用详情第${lineNo}行领用数量不能大于库存数量`,
+                        icon: 'none'
+                    })
+                    return false
+                }
+            }
+            return true
+        },
+        toSubmitData() {
+            const data = JSON.parse(JSON.stringify(this.inputForm))
+            data.collectDate = this.formatDate(this.inputForm.collectDate)
+            data.detailInfos = (this.inputForm.detailInfos || []).map(item => ({
+                ...JSON.parse(JSON.stringify(item)),
+                produceDate: this.formatDate(item.produceDate)
+            }))
+            return data
+        },
+        async saveForm(callback) {
+            return new Promise((resolve, reject) => {
+                // 表单规则验证
+                // ...
+
+                let errors = [];
+
+                if (this.isNotEmpty(this.inputForm.collectDate)) {
+                    this.inputForm.collectDate = this.formatDate(this.inputForm.collectDate);
+                }
+
+                if (this.isEmpty(this.inputForm.detailInfos)) {
+                    errors.push('至少填写一条领用详情信息');
+                } else {
+                    let i = this.inputForm.detailInfos.length;
+                    for (let j = 0; j < i; j++) {
+                        let k = j + 1;
+                        if (this.isEmpty(this.inputForm.detailInfos[j].recipientAgent)) {
+                            errors.push('领用详情第' + k + '行请选择领用人');
+                        } else if (this.isEmpty(this.inputForm.detailInfos[j].collectType)) {
+                            errors.push('领用详情第' + k + '行请选择领用类型');
+                        } else if (this.isEmpty(this.inputForm.detailInfos[j].goodsName)) {
+                            errors.push('领用详情第' + k + '行请选择物品名称');
+                        } else if (this.isEmpty(this.inputForm.detailInfos[j].collectNumber)) {
+                            errors.push('领用详情第' + k + '行请输入领用数量');
+                        }
+
+                        if (parseFloat(this.inputForm.detailInfos[j].collectNumber) > parseFloat(this.inputForm.detailInfos[j].surplusNumber) * parseFloat(this.inputForm.detailInfos[j].spec)) {
+                            errors.push('领用详情第' + k + '行请输入领用数量');
+                        }
+                    }
+                }
+
+                if (errors.length > 0) {
+                    // 存在错误,显示提示信息
+                    errors.forEach(error => {
+                        uni.showToast({
+                            title: error,
+                            icon: 'none',
+                            duration: 2000
+                        });
+                    });
+                    reject('Form validation failed');
+                } else {
+                    // 所有验证通过,执行保存操作
+                    this.$refs.inputForm.validate().then(async () => {
+                        uni.showLoading();
+                        try {
+                            const submitData = this.toSubmitData()
+                            submitData.status = '2'
+                            this.inputForm.status = '2'
+                            const data = await this.collectService.save(submitData)
+                            callback(data.businessTable, data.businessId, submitData)
+                            resolve('Form saved successfully');
+                        } finally {
+                            reject('Save operation failed');
+                        }
+                    }).catch(() => {
+                        reject('Form validation failed');
+                    });
+                }
+            });
+        },
+        async startForm(callback) {
+            this.loading = true
+            try {
+                if (this.isNotEmpty(this.inputForm.id)) {
+                    const data = await this.collectService.findById(this.inputForm.id)
+                    if (this.isNotEmpty(data.status) && !['0', '1', '3'].includes(data.status)) {
+                        uni.showToast({
+                            title: '任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在',
+                            icon: 'none'
+                        })
+                        throw new Error('invalid status')
+                    }
+                }
+                await this.startFormTrue(callback)
+            } finally {
+                this.loading = false
+            }
+        },
+        async startFormTrue(callback) {
+            this.normalizeDateFieldsBeforeValidate()
+            await this.$refs.inputForm.validate()
+            if (!this.validateDetailInfos()) {
+                return
+            }
+            const submitData = this.toSubmitData()
+            submitData.status = '2'
+            this.inputForm.status = '2'
+            const data = await this.collectService.save(submitData)
+            this.inputForm.id = data.businessId
+            callback(data.businessTable, data.businessId, submitData)
+        },
+        async reapplyForm(callback) {
+            this.loading = true
+            try {
+                const data = await this.collectService.findById(this.inputForm.id)
+                if (data.status !== '4') {
+                    uni.showToast({
+                        title: '任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在',
+                        icon: 'none'
+                    })
+                    throw new Error('invalid status')
+                }
+                await this.startFormTrue(callback)
+            } finally {
+                this.loading = false
+            }
+        },
+        async agreeForm(callback) {
+            this.loading = true
+            try {
+                this.normalizeDateFieldsBeforeValidate()
+                await this.$refs.inputForm.validate()
+                if (!this.validateDetailInfos()) {
+                    return
+                }
+                const submitData = this.toSubmitData()
+                try {
+                    const taskName = await this.commonApi.getTaskNameByProcInsId(this.inputForm.procInsId)
+                    if (this.isNotEmpty(taskName) && taskName === '仓库管理员审核') {
+                        submitData.status = '5'
+                        this.inputForm.status = '5'
+                    }
+                } catch (e) {
+                }
+                const data = await this.collectService.save(submitData)
+                callback(data.businessTable, data.businessId, submitData)
+            } finally {
+                this.loading = false
+            }
+        },
+        async updateStatusById(type, callback) {
+            this.loading = true
+            try {
+                if (type === 'reject' || type === 'reback') {
+                    const data = await this.collectService.findById(this.inputForm.id)
+                    if (data.status !== '2') {
+                        this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
+                        throw new Error('invalid status')
+                    }
+                    const nextStatus = type === 'reject' ? '4' : '3'
+                    this.inputForm.status = nextStatus
+                    await this.collectService.updateStatusById({
+                        status: nextStatus,
+                        id: this.inputForm.id
+                    })
+                    callback()
+                } else if (type === 'hold') {
+                    const data = await this.collectService.findById(this.inputForm.id)
+                    if (data.status !== '4') {
+                        this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
+                        throw new Error('invalid status')
+                    }
+                    this.inputForm.status = '1'
+                    await this.collectService.updateStatusById({
+                        status: '1',
+                        id: this.inputForm.id
+                    })
+                    callback()
+                }
+            } finally {
+                this.loading = false
+            }
+        },
+        async openPicker(index) {
+            if (this.isEmpty(this.inputForm.detailInfos[index].collectTypeId)) {
+                uni.showToast({
+                    title: '请先选择领用类型',
+                    icon: 'none'
+                })
+                return
+            }
+            await this.$refs.goodsSelect.loadGoodsList()
+        },
+    }
+}
+</script>
+
+<style scoped>
+.section-wrap {
+    margin-top: 24rpx;
+}
+
+.section-title {
+    margin-bottom: 8rpx;
+    padding-left: 8rpx;
+    font-size: 30rpx;
+    font-weight: 700;
+    color: #1f2937;
+    border-left: 6rpx solid #2979ff;
+}
+
+.section-tip {
+    margin-bottom: 16rpx;
+    font-size: 24rpx;
+    color: #64748b;
+}
+
+.detail-card {
+    margin-bottom: 20rpx;
+    padding: 24rpx;
+    background: #fff;
+    border-radius: 20rpx;
+    box-shadow: 0 10rpx 30rpx rgba(15, 23, 42, 0.05);
+}
+
+.detail-card-title {
+    margin-bottom: 12rpx;
+    font-size: 28rpx;
+    font-weight: 600;
+    color: #0f172a;
+}
+</style>

+ 224 - 0
pages/psiManagement/collect/CollectGoodsSelector.vue

@@ -0,0 +1,224 @@
+<template>
+    <view>
+        <u-form-item label="物品名称" :prop="'detailInfos[' + rowIndex + '].goodsName'" :required="true">
+            <u--input :value="currentValue" placeholder="请选择物品名称" :disabled="disabled" @focus="openPicker"></u--input>
+        </u-form-item>
+
+        <view class="selector-mask" :class="{ show: visible }" @tap="closePicker"></view>
+        <view class="selector-panel" :class="{ show: visible }">
+            <view class="selector-header">
+                <view class="selector-action" @tap="closePicker">取消</view>
+                <view class="selector-title">选择物品</view>
+                <view class="selector-action selector-action-placeholder">取消</view>
+            </view>
+            <view class="selector-search">
+                <u-search :show-action="false" v-model="keyword" placeholder="搜索物品名称"></u-search>
+            </view>
+            <scroll-view scroll-y class="selector-list">
+                <view v-if="loading" class="selector-state">加载中...</view>
+                <view v-else-if="filteredList.length === 0" class="selector-state">暂无可选物品</view>
+                <view v-else class="selector-list-inner">
+                    <view class="selector-card" v-for="item in filteredList" :key="item.id || item.tradeName"
+                        @tap="selectItem(item)">
+                        <view class="selector-name">{{ item.tradeName || '-' }}</view>
+                    </view>
+                </view>
+            </scroll-view>
+        </view>
+    </view>
+</template>
+
+<script>
+import WareHouseService from '@/api/psi/WareHouseService'
+
+export default {
+    name: 'PsiCollectGoodsSelector',
+    props: {
+        rowIndex: Number,
+        inputForm: Object,
+        disabled: Boolean
+    },
+    data() {
+        return {
+            visible: false,
+            loading: false,
+            keyword: '',
+            goodsList: [],
+            wareHouseService: null
+        }
+    },
+    computed: {
+        currentRow() {
+            return (this.inputForm.detailInfos || [])[this.rowIndex] || {}
+        },
+        currentValue() {
+            return this.currentRow.goodsName || ''
+        },
+        filteredList() {
+            const keyword = (this.keyword || '').trim().toLowerCase()
+            if (!keyword) {
+                return this.goodsList
+            }
+            return this.goodsList.filter(item => (item.tradeName || '').toLowerCase().includes(keyword))
+        }
+    },
+    created() {
+        this.wareHouseService = new WareHouseService()
+    },
+    methods: {
+        isEmpty(value) {
+            if (value === null || value === undefined) {
+                return true
+            }
+            if (typeof value === 'string' && value.trim() === '') {
+                return true
+            }
+            if (Array.isArray(value) && value.length === 0) {
+                return true
+            }
+            return false
+        },
+        async openPicker() {
+            if (this.disabled) {
+                return
+            }
+            if (this.isEmpty(this.currentRow.collectTypeId)) {
+                uni.showToast({
+                    title: '请先选择领用类型',
+                    icon: 'none'
+                })
+                return
+            }
+            this.visible = true
+            this.keyword = ''
+            await this.loadGoodsList()
+        },
+        closePicker() {
+            this.visible = false
+        },
+        async loadGoodsList() {
+            this.loading = true
+            try {
+                const data = await this.wareHouseService.getByProduceDateAndNameGroup({
+                    current: 1,
+                    size: 1000,
+                    wareHouseType: this.currentRow.collectTypeId
+                })
+                this.goodsList = (data && data.records) || []
+            } catch (e) {
+                this.goodsList = []
+                uni.showToast({
+                    title: '物品数据加载失败',
+                    icon: 'none'
+                })
+            } finally {
+                this.loading = false
+            }
+        },
+        selectItem(item) {
+            this.$emit('selected', {
+                index: this.rowIndex,
+                item
+            })
+            this.closePicker()
+        }
+    }
+}
+</script>
+
+<style scoped>
+.selector-mask {
+    position: fixed;
+    inset: 0;
+    background: rgba(0, 0, 0, 0.35);
+    opacity: 0;
+    visibility: hidden;
+    transition: all 0.25s ease;
+    z-index: 1000;
+}
+
+.selector-mask.show {
+    opacity: 1;
+    visibility: visible;
+}
+
+.selector-panel {
+    position: fixed;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    height: 68vh;
+    background: #f7f9fc;
+    border-radius: 28rpx 28rpx 0 0;
+    transform: translateY(100%);
+    transition: transform 0.25s ease;
+    z-index: 1001;
+    display: flex;
+    flex-direction: column;
+}
+
+.selector-panel.show {
+    transform: translateY(0);
+}
+
+.selector-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 28rpx 32rpx 16rpx;
+    background: #fff;
+    border-bottom: 1rpx solid #eef2f7;
+}
+
+.selector-title {
+    font-size: 32rpx;
+    font-weight: 600;
+    color: #1f2937;
+}
+
+.selector-action {
+    min-width: 80rpx;
+    font-size: 28rpx;
+    color: #2979ff;
+}
+
+.selector-action-placeholder {
+    opacity: 0;
+}
+
+.selector-search {
+    padding: 20rpx 24rpx 8rpx;
+    background: #fff;
+}
+
+.selector-list {
+    flex: 1;
+    padding: 16rpx 24rpx 32rpx;
+}
+
+.selector-list-inner {
+    display: flex;
+    flex-direction: column;
+    gap: 16rpx;
+}
+
+.selector-state {
+    padding-top: 120rpx;
+    text-align: center;
+    font-size: 28rpx;
+    color: #94a3b8;
+}
+
+.selector-card {
+    background: #fff;
+    border-radius: 20rpx;
+    padding: 24rpx;
+    box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.06);
+}
+
+.selector-name {
+    font-size: 30rpx;
+    font-weight: 600;
+    color: #0f172a;
+}
+</style>

+ 686 - 0
pages/psiManagement/loss/LossForm.vue

@@ -0,0 +1,686 @@
+<template>
+    <view>
+        <cu-custom :backUrl="'/pages/index/index'" :isBack="true" bgColor="bg-gradual-blue">
+            <block slot="content">报损申请</block>
+        </cu-custom>
+
+        <u--form :model="inputForm" labelWidth="100px" class="u-form" labelPosition="left" :rules="rules"
+            ref="inputForm">
+            <u-form-item label="报损编号" borderBottom prop="lossNo">
+                <u--input placeholder="自动生成" v-model="inputForm.lossNo" disabled></u--input>
+            </u-form-item>
+            <u-form-item label="经办人" borderBottom prop="handledBy">
+                <u--input v-model="inputForm.handledBy" disabled></u--input>
+            </u-form-item>
+            <u-form-item label="经办部门" borderBottom prop="handledByOfficeName">
+                <u--input v-model="inputForm.handledByOfficeName" disabled></u--input>
+            </u-form-item>
+            <u-form-item label="报损时间" borderBottom prop="lossDate" :required="true">
+                <el-date-picker v-model="inputForm.lossDate" type="date" placeholder="请选择报损时间" style="width:100%"
+                    placement="bottom-start" clearable :disabled="nodeFlag">
+                </el-date-picker>
+            </u-form-item>
+            <u-form-item label="备注" borderBottom prop="remarks">
+                <u--textarea placeholder="请填写备注信息" :rows="4" :maxlength="500" v-model="inputForm.remarks"
+                    :disabled="nodeFlag"></u--textarea>
+            </u-form-item>
+
+            <view class="section-wrap">
+                <view class="section-title">报损明细</view>
+                <view class="section-tip">报损数量按最小单位填写</view>
+
+                <view v-for="(item, index) in inputForm.detailInfos" :key="item.id || index" class="detail-card">
+                    <view class="detail-card-title">报损详情 {{ index + 1 }}</view>
+
+                    <u-form-item label="报损人" :prop="'detailInfos[' + index + '].recipientAgent'" :required="true">
+                        <u--input v-model="inputForm.detailInfos[index].recipientAgent" placeholder="请选择报损人"
+                            :disabled="nodeFlag" @focus="openUserPullForm(index)" clearable></u--input>
+                    </u-form-item>
+                    <u-form-item label="报损人部门" :prop="'detailInfos[' + index + '].recipientOffice'">
+                        <u--input v-model="inputForm.detailInfos[index].recipientOffice" disabled></u--input>
+                    </u-form-item>
+                    <u-form-item label="报损类型" :prop="'detailInfos[' + index + '].collectType'" :required="true">
+                        <u--input v-model="inputForm.detailInfos[index].collectType" placeholder="请选择报损类型"
+                            :disabled="nodeFlag" @focus="openTypePicker(index)" clearable></u--input>
+                    </u-form-item>
+
+                    <loss-goods-selector :rowIndex="index" :inputForm="inputForm" :disabled="nodeFlag"
+                        @selected="handleGoodsSelected"></loss-goods-selector>
+
+                    <u-form-item label="包装规格" :prop="'detailInfos[' + index + '].spec'">
+                        <u--input v-model="inputForm.detailInfos[index].spec" disabled></u--input>
+                    </u-form-item>
+                    <u-form-item label="生产日期" :prop="'detailInfos[' + index + '].produceDate'">
+                        <u--input :value="formatDisplayDate(inputForm.detailInfos[index].produceDate)" disabled>
+                        </u--input>
+                    </u-form-item>
+                    <!-- <u-form-item label="库存数量" :prop="'detailInfos[' + index + '].surplusNumber'">
+                        <u--input v-model="inputForm.detailInfos[index].surplusNumber" disabled></u--input>
+                    </u-form-item> -->
+                    <!-- <u-form-item label="单位" :prop="'detailInfos[' + index + '].company'">
+                        <u--input v-model="inputForm.detailInfos[index].company" disabled></u--input>
+                    </u-form-item> -->
+                    <u-form-item label="报损数量" :prop="'detailInfos[' + index + '].lossNumber'" :required="true">
+                        <u--input v-model="inputForm.detailInfos[index].lossNumber" placeholder="请输入报损数量"
+                            :disabled="nodeFlag" @blur="handleLossNumberBlur(index)"></u--input>
+                    </u-form-item>
+                    <u-form-item label="附件上传">
+                        <UploadComponent :uploadUrl="`${uploadUrl}/public-modules-server/oss/file/webUpload/upload`"
+                            @onRemove="(file, fileList, fileIndex) => handleRemove(file, fileList, index, fileIndex, 'detail')"
+                            @onSuccess="(file, fileList) => handleUploadSuccess(file, fileList, index, 'detail')"
+                            :fileList="inputForm.detailInfos[index].fileInfoLost" :limit="3" :isDelete="nodeFlag"
+                            :isUpload="nodeFlag">
+                        </UploadComponent>
+                    </u-form-item>
+
+                    <u-form-item label="" v-if="!nodeFlag">
+                        <el-button style="width: 100%" type="danger" plain @click="removeDetail(index)">
+                            删除报损详情
+                        </el-button>
+                    </u-form-item>
+                </view>
+
+                <u-form-item label="" v-if="!nodeFlag">
+                    <el-button style="width: 100%" type="primary" @click="addDetail()" plain>新增报损详情</el-button>
+                </u-form-item>
+            </view>
+
+            <u-form-item label="附件">
+                <UploadComponent :uploadUrl="`${uploadUrl}/public-modules-server/oss/file/webUpload/upload`"
+                    @onRemove="(file, fileList, fileIndex) => handleRemove(file, fileList, '', fileIndex, '')"
+                    @onSuccess="(file, fileList) => handleUploadSuccess(file, fileList, '', '')"
+                    :fileList="inputForm.files" :limit="3" :isDelete="nodeFlag" :isUpload="nodeFlag">
+                </UploadComponent>
+            </u-form-item>
+        </u--form>
+
+        <user-select ref="userPicker" @input="handleUserSelected"></user-select>
+        <ba-tree-picker ref="treePicker" :multiple="false" @select-change="selectTypeChange" title="类型选择"
+            :localdata="listData" valueKey="value" textKey="label" childrenKey="children" />
+    </view>
+</template>
+
+<script>
+import { mapState } from 'vuex'
+import userSelect from '@/components/user-select/user-select-radio.vue'
+import baTreePicker from '@/components/ba-tree-picker/ba-tree-picker.vue'
+import UploadComponent from '@/pages/common/UploadComponent.vue'
+import upload from '@/utils/upload.js'
+import MaterialTypeService from '@/api/psi/MaterialTypeService'
+import WareHouseService from '@/api/psi/WareHouseService'
+import LossService from '@/api/psi/LossService'
+import CommonApi from '@/api/common/CommonApi'
+import LossGoodsSelector from './LossGoodsSelector.vue'
+
+export default {
+    name: 'PsiLossForm',
+    components: {
+        userSelect,
+        baTreePicker,
+        UploadComponent,
+        LossGoodsSelector
+    },
+    computed: mapState({
+        userInfo: (state) => state.user.userInfo
+    }),
+    props: {
+        businessId: {
+            type: String,
+            default: ''
+        },
+        formReadOnly: {
+            type: Boolean,
+            default: false
+        },
+        status: {
+            type: String,
+            default: ''
+        },
+        isCc: {
+            type: Boolean,
+            default: false
+        }
+    },
+    data() {
+        return {
+            uploadUrl: upload.UPLOAD_URL,
+            nodeFlag: false,
+            loading: false,
+            listData: [],
+            materialList: [],
+            inputForm: this.createInputForm(),
+            rules: {
+                lossDate: [
+                    {
+                        required: true,
+                        message: '报损时间不能为空',
+                        trigger: ['change']
+                    }
+                ]
+            }
+        }
+    },
+    materialTypeService: null,
+    wareHouseService: null,
+    lossService: null,
+    commonApi: null,
+    created() {
+        this.ensureServices()
+        this.loadMaterialTypes()
+        this.fillUserInfo()
+    },
+    watch: {
+        businessId: {
+            handler() {
+                if (this.businessId) {
+                    this.init(this.businessId)
+                } else {
+                    this.resetForm()
+                    this.nodeFlag = this.formReadOnly || this.status === 'taskFormDetail' || this.status === 'testSee'
+                }
+            },
+            immediate: true,
+            deep: false
+        }
+    },
+    methods: {
+        createInputForm() {
+            return {
+                id: '',
+                procInsId: '',
+                processDefinitionId: '',
+                status: '',
+                lossNo: '',
+                lossDate: '',
+                remarks: '',
+                handledBy: '',
+                handledById: '',
+                handledByOffice: '',
+                handledByOfficeName: '',
+                userId: '',
+                files: [],
+                detailInfos: []
+            }
+        },
+        createDetailRow() {
+            return {
+                id: '',
+                recipientAgent: (this.userInfo && this.userInfo.name) || '',
+                recipientAgentId: (this.userInfo && this.userInfo.id) || '',
+                recipientOffice: (this.userInfo && this.userInfo.officeDTO && this.userInfo.officeDTO.name) || '',
+                collectType: '',
+                collectTypeId: '',
+                goodsName: '',
+                lossNumber: '',
+                company: '',
+                produceDate: '',
+                shelfLife: '',
+                shelfLifeUnit: '',
+                spec: '',
+                surplusNumber: '',
+                currentInventory: '',
+                fileInfoLost: []
+            }
+        },
+        ensureServices() {
+            if (!this.materialTypeService) {
+                this.materialTypeService = new MaterialTypeService()
+            }
+            if (!this.wareHouseService) {
+                this.wareHouseService = new WareHouseService()
+            }
+            if (!this.lossService) {
+                this.lossService = new LossService()
+            }
+            if (!this.commonApi) {
+                this.commonApi = new CommonApi()
+            }
+        },
+        async loadMaterialTypes() {
+            try {
+                const data = await this.materialTypeService.cgList()
+                this.materialList = data || []
+                this.listData = this.buildTree(this.materialList)
+            } catch (e) {
+                this.materialList = []
+                this.listData = []
+            }
+        },
+        fillUserInfo() {
+            if (!this.userInfo) {
+                return
+            }
+            this.inputForm.handledBy = this.inputForm.handledBy || this.userInfo.name
+            this.inputForm.handledById = this.inputForm.handledById || this.userInfo.id
+            this.inputForm.userId = this.inputForm.userId || this.userInfo.id
+            this.inputForm.handledByOffice = this.inputForm.handledByOffice || (this.userInfo.officeDTO && this.userInfo.officeDTO.id) || ''
+            this.inputForm.handledByOfficeName = this.inputForm.handledByOfficeName || (this.userInfo.officeDTO && this.userInfo.officeDTO.name) || ''
+        },
+        resetForm() {
+            this.inputForm = this.createInputForm()
+            this.fillUserInfo()
+        },
+        async init(id) {
+            this.ensureServices()
+            this.resetForm()
+            this.inputForm.id = id
+            this.loading = true
+            try {
+                const data = await this.lossService.findById(id)
+                this.inputForm = this.normalizeFormData(this.recover(this.createInputForm(), data))
+                this.fillUserInfo()
+                this.nodeFlag = await this.resolveNodeFlag(data)
+            } catch (e) {
+                this.nodeFlag = true
+            } finally {
+                this.loading = false
+            }
+        },
+        async resolveNodeFlag(data) {
+            if (this.formReadOnly || this.status === 'taskFormDetail' || this.status === 'testSee') {
+                return true
+            }
+            try {
+                const taskName = await this.commonApi.getTaskNameByProcInsId((data && data.procInsId) || this.inputForm.procInsId)
+                if (this.isNotEmpty(taskName)) {
+                    return taskName !== '发起人重新发起申请'
+                }
+            } catch (e) {
+            }
+            return false
+        },
+        normalizeFormData(data) {
+            const form = data || this.createInputForm()
+            if (this.isNotEmpty(form.lossDate)) {
+                form.lossDate = new Date(form.lossDate)
+            }
+            form.files = form.files || []
+            form.detailInfos = (form.detailInfos || []).map(item => ({
+                ...item,
+                fileInfoLost: item.fileInfoLost || [],
+                produceDate: this.isNotEmpty(item.produceDate) ? new Date(item.produceDate) : '',
+                surplusNumber: this.isNotEmpty(item.surplusNumber) ? this.formatNumber(item.surplusNumber) : ''
+            }))
+            return form
+        },
+        buildTree(nodes, parentId = '0') {
+            const tree = []
+            for (const node of nodes || []) {
+                if (node.parentId === parentId) {
+                    const children = this.buildTree(nodes, node.id)
+                    if (children.length) {
+                        node.children = children
+                    }
+                    tree.push(node)
+                }
+            }
+            return tree
+        },
+        isEmpty(value) {
+            if (value === null || value === undefined) {
+                return true
+            }
+            if (typeof value === 'string' && value.trim() === '') {
+                return true
+            }
+            if (Array.isArray(value) && value.length === 0) {
+                return true
+            }
+            return false
+        },
+        isNotEmpty(value) {
+            return !this.isEmpty(value)
+        },
+        formatDate(date) {
+            if (this.isEmpty(date)) {
+                return ''
+            }
+            const dateValue = new Date(date)
+            if (Number.isNaN(dateValue.getTime())) {
+                return ''
+            }
+            const year = dateValue.getFullYear()
+            const month = `${dateValue.getMonth() + 1}`.padStart(2, '0')
+            const day = `${dateValue.getDate()}`.padStart(2, '0')
+            return `${year}-${month}-${day}`
+        },
+        formatDisplayDate(date) {
+            return this.formatDate(date)
+        },
+        normalizeDateFieldsBeforeValidate() {
+            if (this.isNotEmpty(this.inputForm.lossDate)) {
+                this.inputForm.lossDate = this.formatDate(this.inputForm.lossDate)
+            }
+        },
+        formatNumber(value) {
+            const numberValue = Number(value || 0)
+            if (Number.isNaN(numberValue)) {
+                return ''
+            }
+            return numberValue % 1 === 0 ? String(numberValue) : numberValue.toFixed(2).replace(/\.?0+$/, '')
+        },
+        formatNumberInput(inputValue, decimalLimit = 2) {
+            if (this.isEmpty(inputValue)) {
+                return ''
+            }
+            const valueText = String(inputValue)
+            if (!/^\d*\.?\d*$/.test(valueText)) {
+                return ''
+            }
+            let value = valueText.replace(/[^\d.]/g, '')
+            const dotIndex = value.indexOf('.')
+            if (dotIndex !== -1) {
+                const substr = value.substr(dotIndex + 1)
+                if (substr.indexOf('.') !== -1) {
+                    value = value.substr(0, dotIndex + 1) + substr.replace(/\./g, '')
+                }
+            }
+            if (dotIndex !== -1) {
+                const integerPart = value.substring(0, dotIndex)
+                const decimalPart = value.substring(dotIndex + 1, dotIndex + 1 + decimalLimit)
+                value = integerPart + '.' + decimalPart
+            }
+            return value
+        },
+        addDetail() {
+            if (this.inputForm.detailInfos.length > 0) {
+                this.$message.warning('报损明细只能保留一条')
+                return
+            }
+            this.inputForm.detailInfos.push(this.createDetailRow())
+        },
+        removeDetail(index) {
+            this.inputForm.detailInfos.splice(index, 1)
+        },
+        patchDetail(index, patch) {
+            const detail = (this.inputForm.detailInfos || [])[index] || {}
+            this.$set(this.inputForm.detailInfos, index, {
+                ...detail,
+                ...patch
+            })
+        },
+        openUserPullForm(index) {
+            if (this.nodeFlag) {
+                return
+            }
+            this.$refs.userPicker.open(index, 'detail')
+        },
+        handleUserSelected(data, index, type) {
+            if (type !== 'detail') {
+                return
+            }
+            this.patchDetail(index, {
+                recipientAgentId: data.id,
+                recipientAgent: data.label,
+                recipientOffice: data.parentLabel
+            })
+        },
+        openTypePicker(index) {
+            if (this.nodeFlag) {
+                return
+            }
+            this.$refs.treePicker._show(index)
+        },
+        selectTypeChange(ids, names, index) {
+            this.patchDetail(index, {
+                collectType: names,
+                collectTypeId: ids[0],
+                goodsName: '',
+                company: '',
+                produceDate: '',
+                shelfLife: '',
+                shelfLifeUnit: '',
+                spec: '',
+                surplusNumber: '',
+                currentInventory: '',
+                lossNumber: ''
+            })
+        },
+        async handleGoodsSelected({ index, item }) {
+            this.patchDetail(index, {
+                goodsName: item.tradeName || '',
+                company: item.company || '',
+                produceDate: this.isNotEmpty(item.produceDate) ? new Date(item.produceDate) : '',
+                shelfLife: item.shelfLife || '',
+                shelfLifeUnit: item.shelfLifeUnit || '',
+                spec: item.spec || '',
+                currentInventory: item.currentInventory || ''
+            })
+            await this.syncDetailStock(index)
+        },
+        async syncDetailStock(index) {
+            const detail = (this.inputForm.detailInfos || [])[index]
+            if (!detail || this.isEmpty(detail.goodsName) || this.isEmpty(detail.collectTypeId)) {
+                return
+            }
+            try {
+                const data = await this.wareHouseService.getByProduceDateNotMerge({
+                    current: 1,
+                    size: 1000,
+                    tradeName: detail.goodsName,
+                    wareHouseType: detail.collectTypeId
+                })
+                const records = (data && data.records) || []
+                const total = records.reduce((sum, item) => {
+                    const currentInventory = Number(item.currentInventory || 0)
+                    const spec = Number(item.spec || 1)
+                    return sum + currentInventory * (spec > 0 ? spec : 1)
+                }, 0)
+                this.$set(this.inputForm.detailInfos[index], 'surplusNumber', this.formatNumber(total))
+            } catch (e) {
+                this.$set(this.inputForm.detailInfos[index], 'surplusNumber', '')
+                uni.showToast({
+                    title: '库存数量加载失败',
+                    icon: 'none'
+                })
+            }
+        },
+        handleUploadSuccess(file, fileList, index, type) {
+            if (type === 'detail') {
+                this.inputForm.detailInfos[index].fileInfoLost = fileList
+            } else {
+                this.inputForm.files = fileList
+            }
+        },
+        handleRemove(file, fileList, lineIndex, fileIndex, type) {
+            if (type === 'detail') {
+                this.inputForm.detailInfos[lineIndex].fileInfoLost.splice(fileIndex, 1)
+            } else {
+                this.inputForm.files.splice(fileIndex, 1)
+            }
+        },
+        handleLossNumberBlur(index) {
+            const detail = (this.inputForm.detailInfos || [])[index]
+            if (!detail) {
+                return
+            }
+            detail.lossNumber = this.formatNumberInput(detail.lossNumber)
+            if (this.isNotEmpty(detail.surplusNumber) && this.isNotEmpty(detail.lossNumber) &&
+                Number(detail.lossNumber) > Number(detail.surplusNumber)) {
+                this.$message.error('报损数量不能大于当前库存')
+                detail.lossNumber = ''
+            }
+        },
+        validateDetailInfos(checkStock = true) {
+            if (this.isEmpty(this.inputForm.detailInfos)) {
+                this.$message.error('请填写报损明细')
+                return false
+            }
+            if (this.inputForm.detailInfos.length !== 1) {
+                this.$message.error('报损明细只能保留一条')
+                return false
+            }
+            const detail = this.inputForm.detailInfos[0]
+            if (this.isEmpty(detail.recipientAgentId)) {
+                this.$message.error('请选择报损人')
+                return false
+            }
+            if (this.isEmpty(detail.collectTypeId)) {
+                this.$message.error('请选择报损类型')
+                return false
+            }
+            if (this.isEmpty(detail.goodsName)) {
+                this.$message.error('请选择物品名称')
+                return false
+            }
+            if (this.isEmpty(detail.lossNumber)) {
+                this.$message.error('请输入报损数量')
+                return false
+            }
+            if (checkStock && this.isNotEmpty(detail.surplusNumber) && Number(detail.lossNumber) > Number(detail.surplusNumber)) {
+                this.$message.error('报损数量不能大于当前库存')
+                return false
+            }
+            return true
+        },
+        toSubmitData() {
+            const data = JSON.parse(JSON.stringify(this.inputForm))
+            data.lossDate = this.formatDate(this.inputForm.lossDate)
+            data.detailInfos = (this.inputForm.detailInfos || []).map(item => ({
+                ...JSON.parse(JSON.stringify(item)),
+                produceDate: this.formatDate(item.produceDate)
+            }))
+            return data
+        },
+        async saveForm(callback) {
+            this.loading = true
+            try {
+                const submitData = this.toSubmitData()
+                submitData.status = '1'
+                this.inputForm.status = '1'
+                const data = await this.lossService.save(submitData)
+                callback(data.businessTable, data.businessId, submitData)
+            } finally {
+                this.loading = false
+            }
+        },
+        async startForm(callback) {
+            this.loading = true
+            try {
+                if (this.isNotEmpty(this.inputForm.id)) {
+                    const data = await this.lossService.findById(this.inputForm.id)
+                    if (this.isNotEmpty(data.status) && !['0', '1', '3'].includes(data.status)) {
+                        this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
+                        throw new Error('invalid status')
+                    }
+                }
+                await this.startFormTrue(callback)
+            } finally {
+                this.loading = false
+            }
+        },
+        async startFormTrue(callback) {
+            this.normalizeDateFieldsBeforeValidate()
+            await this.$refs.inputForm.validate()
+            if (!this.validateDetailInfos(true)) {
+                return
+            }
+            const submitData = this.toSubmitData()
+            submitData.status = '2'
+            this.inputForm.status = '2'
+            const data = await this.lossService.save(submitData)
+            this.inputForm.id = data.businessId
+            callback(data.businessTable, data.businessId, submitData)
+        },
+        async reapplyForm(callback) {
+            this.loading = true
+            try {
+                const data = await this.lossService.findById(this.inputForm.id)
+                if (data.status !== '4') {
+                    this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
+                    throw new Error('invalid status')
+                }
+                await this.startFormTrue(callback)
+            } finally {
+                this.loading = false
+            }
+        },
+        async agreeForm(callback) {
+            this.loading = true
+            try {
+                this.normalizeDateFieldsBeforeValidate()
+                await this.$refs.inputForm.validate()
+                if (!this.validateDetailInfos(false)) {
+                    return
+                }
+                const submitData = this.toSubmitData()
+                submitData.status = '5'
+                this.inputForm.status = '5'
+                const data = await this.lossService.save(submitData)
+                callback(data.businessTable, data.businessId, submitData)
+            } finally {
+                this.loading = false
+            }
+        },
+        async updateStatusById(type, callback) {
+            this.loading = true
+            try {
+                if (type === 'reject' || type === 'reback') {
+                    const data = await this.lossService.findById(this.inputForm.id)
+                    if (data.status !== '2') {
+                        this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
+                        throw new Error('invalid status')
+                    }
+                    const nextStatus = type === 'reject' ? '4' : '3'
+                    this.inputForm.status = nextStatus
+                    await this.lossService.updateStatusById({
+                        status: nextStatus,
+                        id: this.inputForm.id
+                    })
+                    callback()
+                } else if (type === 'hold') {
+                    const data = await this.lossService.findById(this.inputForm.id)
+                    if (data.status !== '4') {
+                        this.$message.error('任务数据已发生改变或不存在,请在待办任务中确认此任务是否存在')
+                        throw new Error('invalid status')
+                    }
+                    this.inputForm.status = '1'
+                    await this.lossService.updateStatusById({
+                        status: '1',
+                        id: this.inputForm.id
+                    })
+                    callback()
+                }
+            } finally {
+                this.loading = false
+            }
+        }
+    }
+}
+</script>
+
+<style scoped>
+.section-wrap {
+    margin-top: 24rpx;
+}
+
+.section-title {
+    margin-bottom: 8rpx;
+    padding-left: 8rpx;
+    font-size: 30rpx;
+    font-weight: 700;
+    color: #1f2937;
+    border-left: 6rpx solid #2979ff;
+}
+
+.section-tip {
+    margin-bottom: 16rpx;
+    font-size: 24rpx;
+    color: #64748b;
+}
+
+.detail-card {
+    margin-bottom: 20rpx;
+    padding: 24rpx;
+    background: #fff;
+    border-radius: 20rpx;
+    box-shadow: 0 10rpx 30rpx rgba(15, 23, 42, 0.05);
+}
+
+.detail-card-title {
+    margin-bottom: 12rpx;
+    font-size: 28rpx;
+    font-weight: 600;
+    color: #0f172a;
+}
+</style>

+ 271 - 0
pages/psiManagement/loss/LossGoodsSelector.vue

@@ -0,0 +1,271 @@
+<template>
+    <view>
+        <u-form-item label="物品名称" :prop="'detailInfos[' + rowIndex + '].goodsName'" :required="true">
+            <u--input :value="currentValue" placeholder="请选择物品名称" readonly :disabled="disabled"
+                @focus="openPicker"></u--input>
+        </u-form-item>
+
+        <view class="selector-mask" :class="{ show: visible }" @tap="closePicker"></view>
+        <view class="selector-panel" :class="{ show: visible }">
+            <view class="selector-header">
+                <view class="selector-action" @tap="closePicker">取消</view>
+                <view class="selector-title">选择物品</view>
+                <view class="selector-action selector-action-placeholder">取消</view>
+            </view>
+            <view class="selector-search">
+                <u-search :show-action="false" v-model="keyword" placeholder="搜索物品名称"></u-search>
+            </view>
+            <scroll-view scroll-y class="selector-list">
+                <view v-if="loading" class="selector-state">加载中...</view>
+                <view v-else-if="filteredList.length === 0" class="selector-state">暂无可选物品</view>
+                <view v-else class="selector-list-inner">
+                    <view class="selector-card" v-for="item in filteredList" :key="item.id || item.tradeName + item.produceDate"
+                        @tap="selectItem(item)">
+                        <view class="selector-name">{{ item.tradeName || '-' }}</view>
+                        <view class="selector-meta">
+                            <text>生产日期:{{ formatDisplayDate(item.produceDate) || '暂无' }}</text>
+                            <text>库存:{{ item.stockBottleCount || '0' }}</text>
+                        </view>
+                        <view class="selector-meta">
+                            <text>单位:{{ item.company || '-' }}</text>
+                            <text>规格:{{ item.spec || '-' }}</text>
+                        </view>
+                    </view>
+                </view>
+            </scroll-view>
+        </view>
+    </view>
+</template>
+
+<script>
+import WareHouseService from '@/api/psi/WareHouseService'
+
+export default {
+    name: 'LossGoodsSelector',
+    props: {
+        rowIndex: Number,
+        inputForm: Object,
+        disabled: Boolean
+    },
+    data() {
+        return {
+            visible: false,
+            loading: false,
+            keyword: '',
+            goodsList: [],
+            wareHouseService: null
+        }
+    },
+    computed: {
+        currentRow() {
+            return (this.inputForm.detailInfos || [])[this.rowIndex] || {}
+        },
+        currentValue() {
+            return this.currentRow.goodsName || ''
+        },
+        filteredList() {
+            const keyword = (this.keyword || '').trim().toLowerCase()
+            if (!keyword) {
+                return this.goodsList
+            }
+            return this.goodsList.filter(item => (item.tradeName || '').toLowerCase().includes(keyword))
+        }
+    },
+    created() {
+        this.wareHouseService = new WareHouseService()
+    },
+    methods: {
+        isEmpty(value) {
+            if (value === null || value === undefined) {
+                return true
+            }
+            if (typeof value === 'string' && value.trim() === '') {
+                return true
+            }
+            if (Array.isArray(value) && value.length === 0) {
+                return true
+            }
+            return false
+        },
+        formatDisplayDate(date) {
+            if (this.isEmpty(date)) {
+                return ''
+            }
+            const dateValue = new Date(date)
+            if (Number.isNaN(dateValue.getTime())) {
+                return ''
+            }
+            const year = dateValue.getFullYear()
+            const month = `${dateValue.getMonth() + 1}`.padStart(2, '0')
+            const day = `${dateValue.getDate()}`.padStart(2, '0')
+            return `${year}-${month}-${day}`
+        },
+        formatNumber(value) {
+            const numberValue = Number(value || 0)
+            if (Number.isNaN(numberValue)) {
+                return '0'
+            }
+            return numberValue % 1 === 0 ? String(numberValue) : numberValue.toFixed(2).replace(/\.?0+$/, '')
+        },
+        getStockBottleCount(item) {
+            const currentInventory = Number(item.currentInventory || 0)
+            const spec = Number(item.spec || 1)
+            return this.formatNumber(currentInventory * (spec > 0 ? spec : 1))
+        },
+        async openPicker() {
+            if (this.disabled) {
+                return
+            }
+            if (this.isEmpty(this.currentRow.collectTypeId)) {
+                uni.showToast({
+                    title: '请先选择报损类型',
+                    icon: 'none'
+                })
+                return
+            }
+            this.visible = true
+            this.keyword = ''
+            await this.loadGoodsList()
+        },
+        closePicker() {
+            this.visible = false
+        },
+        async loadGoodsList() {
+            this.loading = true
+            try {
+                const data = await this.wareHouseService.getByProduceDateNotMerge({
+                    current: 1,
+                    size: 1000,
+                    wareHouseType: this.currentRow.collectTypeId
+                })
+                const records = (data && data.records) || []
+                this.goodsList = records.map(item => ({
+                    ...item,
+                    stockBottleCount: this.getStockBottleCount(item)
+                }))
+            } catch (e) {
+                this.goodsList = []
+                uni.showToast({
+                    title: '物品数据加载失败',
+                    icon: 'none'
+                })
+            } finally {
+                this.loading = false
+            }
+        },
+        selectItem(item) {
+            this.$emit('selected', {
+                index: this.rowIndex,
+                item
+            })
+            this.closePicker()
+        }
+    }
+}
+</script>
+
+<style scoped>
+.selector-mask {
+    position: fixed;
+    inset: 0;
+    background: rgba(0, 0, 0, 0.35);
+    opacity: 0;
+    visibility: hidden;
+    transition: all 0.25s ease;
+    z-index: 1000;
+}
+
+.selector-mask.show {
+    opacity: 1;
+    visibility: visible;
+}
+
+.selector-panel {
+    position: fixed;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    height: 70vh;
+    background: #f7f9fc;
+    border-radius: 28rpx 28rpx 0 0;
+    transform: translateY(100%);
+    transition: transform 0.25s ease;
+    z-index: 1001;
+    display: flex;
+    flex-direction: column;
+}
+
+.selector-panel.show {
+    transform: translateY(0);
+}
+
+.selector-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 28rpx 32rpx 16rpx;
+    background: #fff;
+    border-bottom: 1rpx solid #eef2f7;
+}
+
+.selector-title {
+    font-size: 32rpx;
+    font-weight: 600;
+    color: #1f2937;
+}
+
+.selector-action {
+    min-width: 80rpx;
+    font-size: 28rpx;
+    color: #2979ff;
+}
+
+.selector-action-placeholder {
+    opacity: 0;
+}
+
+.selector-search {
+    padding: 20rpx 24rpx 8rpx;
+    background: #fff;
+}
+
+.selector-list {
+    flex: 1;
+    padding: 16rpx 24rpx 32rpx;
+}
+
+.selector-list-inner {
+    display: flex;
+    flex-direction: column;
+    gap: 16rpx;
+}
+
+.selector-state {
+    padding-top: 120rpx;
+    text-align: center;
+    font-size: 28rpx;
+    color: #94a3b8;
+}
+
+.selector-card {
+    background: #fff;
+    border-radius: 20rpx;
+    padding: 24rpx;
+    box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.06);
+}
+
+.selector-name {
+    font-size: 30rpx;
+    font-weight: 600;
+    color: #0f172a;
+}
+
+.selector-meta {
+    display: flex;
+    justify-content: space-between;
+    gap: 16rpx;
+    margin-top: 10rpx;
+    font-size: 24rpx;
+    color: #64748b;
+}
+</style>

+ 258 - 0
pages/psiManagement/purchase/GoodsSelector.vue

@@ -0,0 +1,258 @@
+<template>
+    <view>
+        <u-form-item label="商品名称" :prop="'detailInfos[' + index_experience + '].tradeName'" :required="true">
+            <u--input :value="currentValue" placeholder="请选择商品名称" readonly :disabled="disabled"
+                @focus="openPicker"></u--input>
+        </u-form-item>
+
+        <view class="selector-mask" :class="{ show: visible }" @tap="closePicker"></view>
+        <view class="selector-panel" :class="{ show: visible }">
+            <view class="selector-header">
+                <view class="selector-action" @tap="closePicker">取消</view>
+                <view class="selector-title">选择商品</view>
+                <view class="selector-action selector-action-placeholder">取消</view>
+            </view>
+            <view class="selector-search">
+                <u-search :show-action="false" v-model="keyword" placeholder="搜索商品名称"></u-search>
+            </view>
+            <scroll-view scroll-y class="selector-list">
+                <view v-if="loading" class="selector-state">加载中...</view>
+                <view v-else-if="filteredList.length === 0" class="selector-state">暂无可选商品</view>
+                <view v-else class="selector-list-inner">
+                    <view class="goods-card" v-for="item in filteredList" :key="item.id || item.tradeName"
+                        @tap="selectItem(item)">
+                        <view class="goods-name">{{ item.tradeName }}</view>
+                        <view class="goods-meta" v-if="item.company || item.spec">
+                            <text v-if="item.company">单位:{{ item.company }}</text>
+                            <text v-if="item.spec">规格:{{ item.spec }}</text>
+                        </view>
+                    </view>
+                </view>
+            </scroll-view>
+        </view>
+    </view>
+</template>
+
+<script>
+import MaterialManagementService from '@/api/psi/MaterialManagementService'
+import WareHouseService from '@/api/psi/WareHouseService'
+
+export default {
+    name: 'GoodsSelector',
+    props: {
+        index_experience: Number,
+        inputForm: Object,
+        disabled: Boolean
+    },
+    data() {
+        return {
+            visible: false,
+            loading: false,
+            keyword: '',
+            goodsList: [],
+            materialManagementService: null,
+            wareHouseService: null
+        }
+    },
+    computed: {
+        currentRow() {
+            return (this.inputForm.detailInfos || [])[this.index_experience] || {}
+        },
+        currentValue() {
+            return this.currentRow.tradeName || ''
+        },
+        filteredList() {
+            const keyword = (this.keyword || '').trim().toLowerCase()
+            if (!keyword) {
+                return this.goodsList
+            }
+            return this.goodsList.filter(item => (item.tradeName || '').toLowerCase().includes(keyword))
+        }
+    },
+    created() {
+        this.materialManagementService = new MaterialManagementService()
+        this.wareHouseService = new WareHouseService()
+    },
+    methods: {
+        isEmpty(value) {
+            let result = false
+            if (value == null || value == undefined) {
+                result = true
+            }
+            if (typeof value === 'string' && (value.replace(/\s+/g, '') === '' || value === '')) {
+                result = true
+            }
+            if (typeof value === 'object' && value instanceof Array && value.length === 0) {
+                result = true
+            }
+            return result
+        },
+        async openPicker() {
+            if (this.disabled) {
+                return
+            }
+            if (this.isEmpty(this.currentRow.procurementTypeId)) {
+                uni.showToast({
+                    title: '请先选择采购类型',
+                    icon: 'none'
+                })
+                return
+            }
+            this.visible = true
+            this.keyword = ''
+            await this.loadGoodsList()
+        },
+        closePicker() {
+            this.visible = false
+        },
+        async loadGoodsList() {
+            this.loading = true
+            try {
+                const data = await this.materialManagementService.findTradeByTypeId(this.currentRow.procurementTypeId)
+                this.goodsList = (data || []).map(item => ({
+                    ...item,
+                    tradeName: item.tradeName || ''
+                }))
+            } catch (e) {
+                this.goodsList = []
+                uni.showToast({
+                    title: '商品数据加载失败',
+                    icon: 'none'
+                })
+            } finally {
+                this.loading = false
+            }
+        },
+        async selectItem(item) {
+            let selected = { ...item }
+            try {
+                const data = await this.wareHouseService.wareHouseSummaryList({
+                    current: 1,
+                    size: 20,
+                    tradeName: item.tradeName,
+                    wareHouseType: this.currentRow.procurementTypeId,
+                    orders: []
+                })
+                const records = (data && data.records) || []
+                if (records.length > 0) {
+                    selected = { ...records[0], ...selected }
+                }
+            } catch (e) {
+            }
+
+            this.$emit('selected', {
+                index: this.index_experience,
+                item: selected
+            })
+            this.closePicker()
+        }
+    }
+}
+</script>
+
+<style scoped>
+.selector-mask {
+    position: fixed;
+    inset: 0;
+    background: rgba(0, 0, 0, 0.35);
+    opacity: 0;
+    visibility: hidden;
+    transition: all 0.25s ease;
+    z-index: 1000;
+}
+
+.selector-mask.show {
+    opacity: 1;
+    visibility: visible;
+}
+
+.selector-panel {
+    position: fixed;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    height: 68vh;
+    background: #f7f9fc;
+    border-radius: 28rpx 28rpx 0 0;
+    transform: translateY(100%);
+    transition: transform 0.25s ease;
+    z-index: 1001;
+    display: flex;
+    flex-direction: column;
+}
+
+.selector-panel.show {
+    transform: translateY(0);
+}
+
+.selector-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 28rpx 32rpx 16rpx;
+    background: #fff;
+    border-bottom: 1rpx solid #eef2f7;
+}
+
+.selector-title {
+    font-size: 32rpx;
+    font-weight: 600;
+    color: #1f2937;
+}
+
+.selector-action {
+    min-width: 80rpx;
+    font-size: 28rpx;
+    color: #2979ff;
+}
+
+.selector-action-placeholder {
+    opacity: 0;
+}
+
+.selector-search {
+    padding: 20rpx 24rpx 8rpx;
+    background: #fff;
+}
+
+.selector-list {
+    flex: 1;
+    padding: 16rpx 24rpx 32rpx;
+}
+
+.selector-list-inner {
+    display: flex;
+    flex-direction: column;
+    gap: 16rpx;
+}
+
+.selector-state {
+    padding-top: 120rpx;
+    text-align: center;
+    font-size: 28rpx;
+    color: #94a3b8;
+}
+
+.goods-card {
+    background: #fff;
+    border-radius: 20rpx;
+    padding: 24rpx;
+    box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.06);
+}
+
+.goods-name {
+    font-size: 30rpx;
+    font-weight: 600;
+    line-height: 1.45;
+    color: #0f172a;
+}
+
+.goods-meta {
+    display: flex;
+    gap: 20rpx;
+    margin-top: 10rpx;
+    font-size: 24rpx;
+    color: #64748b;
+    flex-wrap: wrap;
+}
+</style>

+ 106 - 0
pages/psiManagement/purchase/MyDropdown.vue

@@ -0,0 +1,106 @@
+<template>
+    <u-form-item label="商品名称" :prop="'detailInfos[' + index_experience + '].tradeName'" :required="true">
+        <el-autocomplete
+                style="width: 100%;"
+                v-model="inputForm.detailInfos[index_experience].tradeName"
+                placeholder="请选择物品名称"
+                clearable
+                @focus="showDropdown"
+                :fetch-suggestions="fetchSuggestions"
+                @select="handleSelect"
+                :disabled="disabled"
+        >
+            <template slot-scope="{ item }">
+                <span style="width: 100%;">{{ item.tradeName }}</span>
+            </template>
+        </el-autocomplete>
+    </u-form-item>
+</template>
+
+<script>
+    import materialManagementService from '@/api/materialManagement/MaterialManagementService'
+    export default {
+        props: {
+            index_experience: Number,
+            inputForm: Object,
+            disabled: Boolean // 接收传入的 disabled 属性
+        },
+        data() {
+            return {
+                showDropdownList: false,
+                dropdownList: [],
+                selectedTradeName: ''
+            };
+        },
+        created() {
+        },
+        methods: {
+            showDropdown() {
+                let row = this.inputForm.detailInfos[this.index_experience]
+                row.tradeNameList = []
+                row.tradeNameData = []
+
+                let procurementTypeId = this.inputForm.detailInfos[this.index_experience].procurementTypeId
+
+                if (this.isNotEmpty(procurementTypeId)) {
+                    // Simulated data
+                    materialManagementService.findTradeByTypeId(procurementTypeId)
+                        .then((data) => {
+                            row.tradeNameList = JSON.parse(JSON.stringify(data))
+                            this.dropdownList = JSON.parse(JSON.stringify(data))
+                            this.showDropdownList = true; // Move this line here
+                        })
+                        .catch(() => {
+                            // Handle error
+                        });
+                }
+            },
+            isEmpty(value) {
+                let result = false;
+                if (value == null || value == undefined) {
+                    result = true;
+                }
+                if (typeof value == 'string' && (value.replace(/\s+/g, "") == "" || value == "")) {
+                    result = true;
+                }
+                if (typeof value == "object" && value instanceof Array && value.length === 0) {
+                    result = true;
+                }
+                return result;
+            },
+            isNotEmpty (value) {
+                return !this.isEmpty(value)
+            },
+            fetchSuggestions(queryString, callback) {
+                if (this.dropdownList && queryString) {
+                    const filteredList = this.dropdownList.filter(item =>
+                        item.tradeName.toLowerCase().includes(queryString.toLowerCase())
+                    );
+                    callback(filteredList);
+                } else {
+                    callback(this.dropdownList);
+                    // Handle the case when dropdownList or queryString is not properly initialized
+                }
+            },
+            handleSelect(item) {
+                this.inputForm.detailInfos[this.index_experience].tradeName = item.tradeName;
+                this.showDropdownList = false;
+            }
+        }
+    };
+</script>
+
+<style scoped>
+    .my-dropdown {
+        position: absolute;
+        top: calc(100% + 4px); /* 将下拉框定位在输入框的下方,此处增加了 4px 的间距 */
+        left: 0;
+        width: 100%;
+        background-color: #fff;
+        border: 1px solid #ccc;
+    }
+    .list-item {
+        padding: 8px;
+        cursor: pointer;
+    }
+</style>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1049 - 0
pages/psiManagement/purchase/PurchaseForm.vue


+ 103 - 0
pages/psiManagement/purchase/SupplierSelector.vue

@@ -0,0 +1,103 @@
+<template>
+    <u-form-item label="供应商" :prop="'detailInfos[' + index_experience + '].supplierName'" :required="true">
+        <el-autocomplete
+                style="width: 100%;"
+                v-model="inputForm.detailInfos[index_experience].supplierName"
+                placeholder="请选择供应商"
+                clearable
+                @focus="showDropdown"
+                :fetch-suggestions="fetchSuggestions"
+                @select="handleSelect"
+                :disabled="disabled"
+        >
+            <template slot-scope="{ item }">
+                <span style="width: 100%;">{{ item.name }}</span>
+            </template>
+        </el-autocomplete>
+    </u-form-item>
+</template>
+
+<script>
+    import SupplierService from '@/api/psi/SupplierService'
+    export default {
+        props: {
+            index_experience: Number,
+            inputForm: Object,
+            disabled: Boolean // 接收传入的 disabled 属性
+        },
+        data() {
+            return {
+                showDropdownList: false,
+                selectedSupplier: null,
+                suppliers: [] // 供应商对象数组 [{id: 1, name: 'Supplier 1'}, ...]
+            };
+        },
+        supplierService: null,
+        created () {
+            this.supplierService = new SupplierService()
+            this.showDropdown()
+        },
+        methods: {
+
+            showDropdown() {
+                // 将获取的供应商赋值给 this.suppliers
+                this.supplierService.list({
+                    'current': 1,
+                    'size': -1,
+                }).then((data) => {
+                    this.suppliers = data.records
+                    this.showDropdownList = true;
+                })
+            },
+            isEmpty(value) {
+                let result = false;
+                if (value == null || value == undefined) {
+                    result = true;
+                }
+                if (typeof value == 'string' && (value.replace(/\s+/g, "") == "" || value == "")) {
+                    result = true;
+                }
+                if (typeof value == "object" && value instanceof Array && value.length === 0) {
+                    result = true;
+                }
+                return result;
+            },
+            isNotEmpty (value) {
+                return !this.isEmpty(value)
+            },
+            fetchSuggestions(queryString, callback) {
+
+                if (this.suppliers && queryString) {
+                    const filteredList = this.suppliers.filter(item =>
+                        item.name.toLowerCase().includes(queryString.toLowerCase())
+                    );
+                    callback(filteredList);
+                } else {
+                    callback(this.suppliers);
+                    // Handle the case when suppliers or queryString is not properly initialized
+                }
+            },
+            handleSelect(item) {
+                this.inputForm.detailInfos[this.index_experience].supplierName = item.name;
+                this.inputForm.detailInfos[this.index_experience].supplierId = item.id;
+                this.inputForm.detailInfos[this.index_experience].isSupplier = '2';
+                this.showDropdownList = false;
+            }
+        }
+    };
+</script>
+
+<style scoped>
+    .my-dropdown {
+        position: absolute;
+        top: calc(100% + 4px); /* 将下拉框定位在输入框的下方,此处增加了 4px 的间距 */
+        left: 0;
+        width: 100%;
+        background-color: #fff;
+        border: 1px solid #ccc;
+    }
+    .list-item {
+        padding: 8px;
+        cursor: pointer;
+    }
+</style>

+ 226 - 0
pages/psiManagement/wareHouse/PurchaseSelector.vue

@@ -0,0 +1,226 @@
+<template>
+    <view>
+        <u-form-item label="采购编号" prop="purchaseNo" :required="true">
+            <u--input :value="currentValue" placeholder="请选择采购编号" readonly :disabled="disabled"
+                @focus="openPicker"></u--input>
+        </u-form-item>
+
+        <view class="selector-mask" :class="{ show: visible }" @tap="closePicker"></view>
+        <view class="selector-panel" :class="{ show: visible }">
+            <view class="selector-header">
+                <view class="selector-action" @tap="closePicker">取消</view>
+                <view class="selector-title">选择采购单</view>
+                <view class="selector-action selector-action-placeholder">取消</view>
+            </view>
+            <view class="selector-search">
+                <u-search :show-action="false" v-model="keyword" placeholder="搜索采购编号或采购名称"></u-search>
+            </view>
+            <scroll-view scroll-y class="selector-list">
+                <view v-if="loading" class="selector-state">加载中...</view>
+                <view v-else-if="filteredList.length === 0" class="selector-state">暂无可选采购单</view>
+                <view v-else class="selector-list-inner">
+                    <view class="selector-card" v-for="item in filteredList" :key="item.id || item.purchaseNo"
+                        @tap="selectItem(item)">
+                        <view class="selector-name">{{ item.purchaseNo }}</view>
+                        <view class="selector-desc">{{ item.purchaseSketch || '未填写采购名称' }}</view>
+                        <view class="selector-meta">
+                            <text v-if="item.handledByName">经办人:{{ item.handledByName }}</text>
+                            <text v-if="item.createDate">申请时间:{{ item.createDate }}</text>
+                        </view>
+                    </view>
+                </view>
+            </scroll-view>
+        </view>
+    </view>
+</template>
+
+<script>
+import WareHouseService from '@/api/psi/WareHouseService'
+
+export default {
+    name: 'PurchaseSelector',
+    props: {
+        inputForm: Object,
+        disabled: Boolean
+    },
+    data() {
+        return {
+            visible: false,
+            loading: false,
+            keyword: '',
+            purchaseList: [],
+            wareHouseService: null
+        }
+    },
+    computed: {
+        currentValue() {
+            return (this.inputForm && this.inputForm.purchaseNo) || ''
+        },
+        filteredList() {
+            const keyword = (this.keyword || '').trim().toLowerCase()
+            if (!keyword) {
+                return this.purchaseList
+            }
+            return this.purchaseList.filter(item => {
+                const purchaseNo = (item.purchaseNo || '').toLowerCase()
+                const purchaseSketch = (item.purchaseSketch || '').toLowerCase()
+                return purchaseNo.includes(keyword) || purchaseSketch.includes(keyword)
+            })
+        }
+    },
+    created() {
+        this.wareHouseService = new WareHouseService()
+    },
+    methods: {
+        async openPicker() {
+            if (this.disabled) {
+                return
+            }
+            this.visible = true
+            this.keyword = ''
+            if (!this.purchaseList.length) {
+                await this.loadPurchaseList()
+            }
+        },
+        closePicker() {
+            this.visible = false
+        },
+        async loadPurchaseList() {
+            this.loading = true
+            try {
+                const data = await this.wareHouseService.reimbursementList({
+                    current: 1,
+                    size: 50,
+                    status: '5',
+                    orders: []
+                })
+                this.purchaseList = (data && data.records) || []
+            } catch (e) {
+                this.purchaseList = []
+                uni.showToast({
+                    title: '采购单加载失败',
+                    icon: 'none'
+                })
+            } finally {
+                this.loading = false
+            }
+        },
+        selectItem(item) {
+            this.$emit('selected', item)
+            this.closePicker()
+        }
+    }
+}
+</script>
+
+<style scoped>
+.selector-mask {
+    position: fixed;
+    inset: 0;
+    background: rgba(0, 0, 0, 0.35);
+    opacity: 0;
+    visibility: hidden;
+    transition: all 0.25s ease;
+    z-index: 1000;
+}
+
+.selector-mask.show {
+    opacity: 1;
+    visibility: visible;
+}
+
+.selector-panel {
+    position: fixed;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    height: 72vh;
+    background: #f7f9fc;
+    border-radius: 28rpx 28rpx 0 0;
+    transform: translateY(100%);
+    transition: transform 0.25s ease;
+    z-index: 1001;
+    display: flex;
+    flex-direction: column;
+}
+
+.selector-panel.show {
+    transform: translateY(0);
+}
+
+.selector-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 28rpx 32rpx 16rpx;
+    background: #fff;
+    border-bottom: 1rpx solid #eef2f7;
+}
+
+.selector-title {
+    font-size: 32rpx;
+    font-weight: 600;
+    color: #1f2937;
+}
+
+.selector-action {
+    min-width: 80rpx;
+    font-size: 28rpx;
+    color: #2979ff;
+}
+
+.selector-action-placeholder {
+    opacity: 0;
+}
+
+.selector-search {
+    padding: 20rpx 24rpx 8rpx;
+    background: #fff;
+}
+
+.selector-list {
+    flex: 1;
+    padding: 16rpx 24rpx 32rpx;
+}
+
+.selector-list-inner {
+    display: flex;
+    flex-direction: column;
+    gap: 16rpx;
+}
+
+.selector-state {
+    padding-top: 120rpx;
+    text-align: center;
+    font-size: 28rpx;
+    color: #94a3b8;
+}
+
+.selector-card {
+    background: #fff;
+    border-radius: 20rpx;
+    padding: 24rpx;
+    box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.06);
+}
+
+.selector-name {
+    font-size: 30rpx;
+    font-weight: 600;
+    color: #0f172a;
+}
+
+.selector-desc {
+    margin-top: 8rpx;
+    font-size: 26rpx;
+    color: #334155;
+}
+
+.selector-meta {
+    display: flex;
+    flex-direction: column;
+    gap: 8rpx;
+    margin-top: 12rpx;
+    font-size: 24rpx;
+    color: #64748b;
+}
+</style>

+ 224 - 0
pages/psiManagement/wareHouse/WareHouseGoodsSelector.vue

@@ -0,0 +1,224 @@
+<template>
+    <view>
+        <u-form-item label="商品名称" :prop="'wareHouse[' + rowIndex + '].tradeName'" :required="true">
+            <u--input :value="currentValue" placeholder="请选择商品名称" readonly :disabled="disabled"
+                @focus="openPicker"></u--input>
+        </u-form-item>
+
+        <view class="selector-mask" :class="{ show: visible }" @tap="closePicker"></view>
+        <view class="selector-panel" :class="{ show: visible }">
+            <view class="selector-header">
+                <view class="selector-action" @tap="closePicker">取消</view>
+                <view class="selector-title">选择商品</view>
+                <view class="selector-action selector-action-placeholder">取消</view>
+            </view>
+            <view class="selector-search">
+                <u-search :show-action="false" v-model="keyword" placeholder="搜索商品名称"></u-search>
+            </view>
+            <scroll-view scroll-y class="selector-list">
+                <view v-if="loading" class="selector-state">加载中...</view>
+                <view v-else-if="filteredList.length === 0" class="selector-state">暂无可选商品</view>
+                <view v-else class="selector-list-inner">
+                    <view class="selector-card" v-for="item in filteredList" :key="item.id || item.tradeName"
+                        @tap="selectItem(item)">
+                        <view class="selector-name">{{ item.tradeName }}</view>
+                    </view>
+                </view>
+            </scroll-view>
+        </view>
+    </view>
+</template>
+
+<script>
+import WareHouseService from '@/api/psi/WareHouseService'
+
+export default {
+    name: 'WareHouseGoodsSelector',
+    props: {
+        rowIndex: Number,
+        inputForm: Object,
+        disabled: Boolean
+    },
+    data() {
+        return {
+            visible: false,
+            loading: false,
+            keyword: '',
+            goodsList: [],
+            wareHouseService: null
+        }
+    },
+    computed: {
+        currentRow() {
+            return (this.inputForm.wareHouse || [])[this.rowIndex] || {}
+        },
+        currentValue() {
+            return this.currentRow.tradeName || ''
+        },
+        filteredList() {
+            const keyword = (this.keyword || '').trim().toLowerCase()
+            if (!keyword) {
+                return this.goodsList
+            }
+            return this.goodsList.filter(item => (item.tradeName || '').toLowerCase().includes(keyword))
+        }
+    },
+    created() {
+        this.wareHouseService = new WareHouseService()
+    },
+    methods: {
+        isEmpty(value) {
+            if (value === null || value === undefined) {
+                return true
+            }
+            if (typeof value === 'string' && value.trim() === '') {
+                return true
+            }
+            if (Array.isArray(value) && value.length === 0) {
+                return true
+            }
+            return false
+        },
+        async openPicker() {
+            if (this.disabled) {
+                return
+            }
+            if (this.isEmpty(this.currentRow.wareHouseTypeId)) {
+                uni.showToast({
+                    title: '请先选择入库类型',
+                    icon: 'none'
+                })
+                return
+            }
+            this.visible = true
+            this.keyword = ''
+            await this.loadGoodsList()
+        },
+        closePicker() {
+            this.visible = false
+        },
+        async loadGoodsList() {
+            this.loading = true
+            try {
+                const data = await this.wareHouseService.findTradeByTypeId(this.currentRow.wareHouseTypeId)
+                this.goodsList = (data || []).map(item => ({
+                    ...item,
+                    tradeName: item.tradeName || ''
+                }))
+            } catch (e) {
+                this.goodsList = []
+                uni.showToast({
+                    title: '商品数据加载失败',
+                    icon: 'none'
+                })
+            } finally {
+                this.loading = false
+            }
+        },
+        selectItem(item) {
+            this.$emit('selected', {
+                index: this.rowIndex,
+                item
+            })
+            this.closePicker()
+        }
+    }
+}
+</script>
+
+<style scoped>
+.selector-mask {
+    position: fixed;
+    inset: 0;
+    background: rgba(0, 0, 0, 0.35);
+    opacity: 0;
+    visibility: hidden;
+    transition: all 0.25s ease;
+    z-index: 1000;
+}
+
+.selector-mask.show {
+    opacity: 1;
+    visibility: visible;
+}
+
+.selector-panel {
+    position: fixed;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    height: 68vh;
+    background: #f7f9fc;
+    border-radius: 28rpx 28rpx 0 0;
+    transform: translateY(100%);
+    transition: transform 0.25s ease;
+    z-index: 1001;
+    display: flex;
+    flex-direction: column;
+}
+
+.selector-panel.show {
+    transform: translateY(0);
+}
+
+.selector-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 28rpx 32rpx 16rpx;
+    background: #fff;
+    border-bottom: 1rpx solid #eef2f7;
+}
+
+.selector-title {
+    font-size: 32rpx;
+    font-weight: 600;
+    color: #1f2937;
+}
+
+.selector-action {
+    min-width: 80rpx;
+    font-size: 28rpx;
+    color: #2979ff;
+}
+
+.selector-action-placeholder {
+    opacity: 0;
+}
+
+.selector-search {
+    padding: 20rpx 24rpx 8rpx;
+    background: #fff;
+}
+
+.selector-list {
+    flex: 1;
+    padding: 16rpx 24rpx 32rpx;
+}
+
+.selector-list-inner {
+    display: flex;
+    flex-direction: column;
+    gap: 16rpx;
+}
+
+.selector-state {
+    padding-top: 120rpx;
+    text-align: center;
+    font-size: 28rpx;
+    color: #94a3b8;
+}
+
+.selector-card {
+    background: #fff;
+    border-radius: 20rpx;
+    padding: 24rpx;
+    box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.06);
+}
+
+.selector-name {
+    font-size: 30rpx;
+    font-weight: 600;
+    color: #0f172a;
+}
+</style>

+ 219 - 0
pages/psiManagement/wareHouse/WareHouseSupplierSelector.vue

@@ -0,0 +1,219 @@
+<template>
+    <view>
+        <u-form-item label="供应商" :prop="'wareHouse[' + rowIndex + '].supplierName'" :required="true">
+            <u--input :value="currentValue" placeholder="请选择供应商" readonly :disabled="disabled"
+                @focus="openPicker"></u--input>
+        </u-form-item>
+
+        <view class="selector-mask" :class="{ show: visible }" @tap="closePicker"></view>
+        <view class="selector-panel" :class="{ show: visible }">
+            <view class="selector-header">
+                <view class="selector-action" @tap="closePicker">取消</view>
+                <view class="selector-title">选择供应商</view>
+                <view class="selector-action selector-action-placeholder">取消</view>
+            </view>
+            <view class="selector-search">
+                <u-search :show-action="false" v-model="keyword" placeholder="搜索供应商名称"></u-search>
+            </view>
+            <scroll-view scroll-y class="selector-list">
+                <view v-if="loading" class="selector-state">加载中...</view>
+                <view v-else-if="filteredList.length === 0" class="selector-state">暂无可选供应商</view>
+                <view v-else class="selector-list-inner">
+                    <view class="selector-card" v-for="item in filteredList" :key="item.id" @tap="selectItem(item)">
+                        <view class="selector-name">{{ item.name }}</view>
+                        <view class="selector-meta">
+                            <text v-if="item.address">地址:{{ item.address }}</text>
+                            <text v-if="item.telPhone">联系电话:{{ item.telPhone }}</text>
+                        </view>
+                    </view>
+                </view>
+            </scroll-view>
+        </view>
+    </view>
+</template>
+
+<script>
+import SupplierService from '@/api/psi/SupplierService'
+
+export default {
+    name: 'WareHouseSupplierSelector',
+    props: {
+        rowIndex: Number,
+        inputForm: Object,
+        disabled: Boolean
+    },
+    data() {
+        return {
+            visible: false,
+            loading: false,
+            keyword: '',
+            suppliers: [],
+            supplierService: null
+        }
+    },
+    computed: {
+        currentRow() {
+            return (this.inputForm.wareHouse || [])[this.rowIndex] || {}
+        },
+        currentValue() {
+            return this.currentRow.supplierName || ''
+        },
+        filteredList() {
+            const keyword = (this.keyword || '').trim().toLowerCase()
+            if (!keyword) {
+                return this.suppliers
+            }
+            return this.suppliers.filter(item => (item.name || '').toLowerCase().includes(keyword))
+        }
+    },
+    created() {
+        this.supplierService = new SupplierService()
+    },
+    methods: {
+        async openPicker() {
+            if (this.disabled) {
+                return
+            }
+            this.visible = true
+            this.keyword = ''
+            if (!this.suppliers.length) {
+                await this.loadSuppliers()
+            }
+        },
+        closePicker() {
+            this.visible = false
+        },
+        async loadSuppliers() {
+            this.loading = true
+            try {
+                const data = await this.supplierService.list({
+                    current: 1,
+                    size: -1
+                })
+                this.suppliers = (data && data.records) || []
+            } catch (e) {
+                this.suppliers = []
+                uni.showToast({
+                    title: '供应商数据加载失败',
+                    icon: 'none'
+                })
+            } finally {
+                this.loading = false
+            }
+        },
+        selectItem(item) {
+            this.$emit('selected', {
+                index: this.rowIndex,
+                item
+            })
+            this.closePicker()
+        }
+    }
+}
+</script>
+
+<style scoped>
+.selector-mask {
+    position: fixed;
+    inset: 0;
+    background: rgba(0, 0, 0, 0.35);
+    opacity: 0;
+    visibility: hidden;
+    transition: all 0.25s ease;
+    z-index: 1000;
+}
+
+.selector-mask.show {
+    opacity: 1;
+    visibility: visible;
+}
+
+.selector-panel {
+    position: fixed;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    height: 68vh;
+    background: #f7f9fc;
+    border-radius: 28rpx 28rpx 0 0;
+    transform: translateY(100%);
+    transition: transform 0.25s ease;
+    z-index: 1001;
+    display: flex;
+    flex-direction: column;
+}
+
+.selector-panel.show {
+    transform: translateY(0);
+}
+
+.selector-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 28rpx 32rpx 16rpx;
+    background: #fff;
+    border-bottom: 1rpx solid #eef2f7;
+}
+
+.selector-title {
+    font-size: 32rpx;
+    font-weight: 600;
+    color: #1f2937;
+}
+
+.selector-action {
+    min-width: 80rpx;
+    font-size: 28rpx;
+    color: #2979ff;
+}
+
+.selector-action-placeholder {
+    opacity: 0;
+}
+
+.selector-search {
+    padding: 20rpx 24rpx 8rpx;
+    background: #fff;
+}
+
+.selector-list {
+    flex: 1;
+    padding: 16rpx 24rpx 32rpx;
+}
+
+.selector-list-inner {
+    display: flex;
+    flex-direction: column;
+    gap: 16rpx;
+}
+
+.selector-state {
+    padding-top: 120rpx;
+    text-align: center;
+    font-size: 28rpx;
+    color: #94a3b8;
+}
+
+.selector-card {
+    background: #fff;
+    border-radius: 20rpx;
+    padding: 24rpx;
+    box-shadow: 0 8rpx 24rpx rgba(15, 23, 42, 0.06);
+}
+
+.selector-name {
+    font-size: 30rpx;
+    font-weight: 600;
+    color: #0f172a;
+}
+
+.selector-meta {
+    display: flex;
+    flex-direction: column;
+    gap: 8rpx;
+    margin-top: 12rpx;
+    font-size: 24rpx;
+    color: #64748b;
+}
+</style>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1021 - 0
pages/psiManagement/wareHouse/WareHouseUpdateForm.vue


+ 33 - 2
pages/workbench/task/TaskForm.vue

@@ -5,9 +5,9 @@
 		<view v-show="0 === tabIndex">
 			<scroll-view scroll-y>
 				<view class=" bg-white ">
-
 					<TestActivitiLeaveForm v-if="formUrl.endsWith('TestActivitiLeaveForm')" :formReadOnly="formReadOnly"
-						:class="formReadOnly ? 'readonly' : ''" ref="form" :businessId="businessId"></TestActivitiLeaveForm>
+						:class="formReadOnly ? 'readonly' : ''" ref="form" :businessId="businessId">
+					</TestActivitiLeaveForm>
 					<EnrollmentRegistrationAddForm v-if="formUrl.endsWith('EnrollmentRegistrationAddForm')"
 						:formReadOnly="formReadOnly" :class="formReadOnly ? 'readonly' : ''" ref="form"
 						:businessId="businessId"></EnrollmentRegistrationAddForm>
@@ -26,9 +26,24 @@
 						:formReadOnly="formReadOnly" :class="formReadOnly ? 'readonly' : ''" ref="form"
 						:businessId="businessId"></RegistrationAddForm>
 					<!-- 已完成 -->
+					<PsiCollectForm
+						v-else-if="formUrl.includes('/psiManagement/collect/') && formUrl.endsWith('CollectForm')"
+						:formReadOnly="formReadOnly" :class="formReadOnly ? 'readonly' : ''" ref="form"
+						:businessId="businessId" :status="status"></PsiCollectForm>
 					<CollectForm v-else-if="formUrl.endsWith('CollectForm')" :formReadOnly="formReadOnly"
 						:class="formReadOnly ? 'readonly' : ''" ref="form" :businessId="businessId"></CollectForm>
 					<!-- 已完成 -->
+					<PsiPurchaseForm
+						v-else-if="formUrl.includes('/psiManagement/purchase/') && formUrl.endsWith('PurchaseForm')"
+						:formReadOnly="formReadOnly" :class="formReadOnly ? 'readonly' : ''" ref="form"
+						:businessId="businessId"></PsiPurchaseForm>
+					<PsiWareHouseUpdateForm
+						v-else-if="formUrl.includes('/psiManagement/wareHouse/') && formUrl.endsWith('WareHouseUpdateForm')"
+						:formReadOnly="formReadOnly" :class="formReadOnly ? 'readonly' : ''" ref="form"
+						:businessId="businessId" :status="status"></PsiWareHouseUpdateForm>
+					<PsiLossForm v-else-if="formUrl.includes('/psiManagement/loss/') && formUrl.endsWith('LossForm')"
+						:formReadOnly="formReadOnly" :class="formReadOnly ? 'readonly' : ''" ref="form"
+						:businessId="businessId" :status="status"></PsiLossForm>
 					<PurchaseForm v-else-if="formUrl.endsWith('PurchaseForm')" :formReadOnly="formReadOnly"
 						:class="formReadOnly ? 'readonly' : ''" ref="form" :businessId="businessId"></PurchaseForm>
 					<HolidayForm v-else-if="formUrl.endsWith('HolidayForm')" :formReadOnly="formReadOnly"
@@ -166,6 +181,10 @@ import TestActivitiLeaveForm from '@/pages/test/activiti/TestActivitiLeaveForm.v
 import RegistrationAddForm from '@/pages/human/practice/register/RegistrationAddForm.vue'
 import CollectForm from '@/pages/materialManagement/collect/CollectForm.vue'
 import PurchaseForm from '@/pages/materialManagement/purchase/PurchaseForm.vue'
+import PsiPurchaseForm from '@/pages/psiManagement/purchase/PurchaseForm.vue'
+import PsiWareHouseUpdateForm from '@/pages/psiManagement/wareHouse/WareHouseUpdateForm.vue'
+import PsiLossForm from '@/pages/psiManagement/loss/LossForm.vue'
+import PsiCollectForm from '@/pages/psiManagement/collect/CollectForm.vue'
 import EnrollmentRegistrationAddForm from '@/pages/human/enrollment/registration/EnrollmentRegistrationAddForm.vue'
 import moment from 'moment'
 import taskService from "@/api/flowable/taskService"
@@ -238,8 +257,16 @@ export default {
 					this.form = InvoiceFormTask
 				} else if (this.formUrl.endsWith('RegistrationAddForm')) {
 					this.form = RegistrationAddForm
+				} else if (this.formUrl.includes('/psiManagement/collect/') && this.formUrl.endsWith('CollectForm')) {
+					this.form = PsiCollectForm
 				} else if (this.formUrl.endsWith('CollectForm')) {
 					this.form = CollectForm
+				} else if (this.formUrl.includes('/psiManagement/purchase/') && this.formUrl.endsWith('PurchaseForm')) {
+					this.form = PsiPurchaseForm
+				} else if (this.formUrl.includes('/psiManagement/wareHouse/') && this.formUrl.endsWith('WareHouseUpdateForm')) {
+					this.form = PsiWareHouseUpdateForm
+				} else if (this.formUrl.includes('/psiManagement/loss/') && this.formUrl.endsWith('LossForm')) {
+					this.form = PsiLossForm
 				} else if (this.formUrl.endsWith('PurchaseForm')) {
 					this.form = PurchaseForm
 				} else if (this.formUrl.endsWith('HolidayForm')) {
@@ -307,6 +334,10 @@ export default {
 		RegistrationAddForm,
 		CollectForm,
 		PurchaseForm,
+		PsiPurchaseForm,
+		PsiWareHouseUpdateForm,
+		PsiLossForm,
+		PsiCollectForm,
 		HolidayForm,
 		DepartRegistrationAddForm,
 		HandoverAddForm,

+ 17 - 1
pages/workbench/task/TaskFormDetail.vue

@@ -64,6 +64,10 @@
 	import RegistrationAddForm from '@/pages/human/practice/register/RegistrationAddForm.vue'
 	import CollectForm from '@/pages/materialManagement/collect/CollectForm.vue'
 	import PurchaseForm from '@/pages/materialManagement/purchase/PurchaseForm.vue'
+	import PsiPurchaseForm from '@/pages/psiManagement/purchase/PurchaseForm.vue'
+	import PsiWareHouseUpdateForm from '@/pages/psiManagement/wareHouse/WareHouseUpdateForm.vue'
+	import PsiLossForm from '@/pages/psiManagement/loss/LossForm.vue'
+	import PsiCollectForm from '@/pages/psiManagement/collect/CollectForm.vue'
 	import EnrollmentRegistrationAddForm from '@/pages/human/enrollment/registration/EnrollmentRegistrationAddForm.vue'
 	import taskService from "@/api/flowable/taskService"
 	import formService from "@/api/flowable/formService"
@@ -108,8 +112,16 @@
 						this.form = EnrollmentRegistrationAddForm
 					}else if(this.formUrl.endsWith('RegistrationAddForm')){ 
 						this.form = RegistrationAddForm
+					}else if(this.formUrl.includes('/psiManagement/collect/') && this.formUrl.endsWith('CollectForm')){
+						this.form = PsiCollectForm
 					}else if(this.formUrl.endsWith('CollectForm')){
 						this.form = CollectForm
+					}else if(this.formUrl.includes('/psiManagement/purchase/') && this.formUrl.endsWith('PurchaseForm')){
+						this.form = PsiPurchaseForm
+					}else if(this.formUrl.includes('/psiManagement/wareHouse/') && this.formUrl.endsWith('WareHouseUpdateForm')){
+						this.form = PsiWareHouseUpdateForm
+					}else if(this.formUrl.includes('/psiManagement/loss/') && this.formUrl.endsWith('LossForm')){
+						this.form = PsiLossForm
 					}else if(this.formUrl.endsWith('PurchaseForm')){
 						this.form = PurchaseForm
 					}else if(this.formUrl.endsWith('HolidayForm')){
@@ -158,6 +170,10 @@
 		  RegistrationAddForm,
 			CollectForm,
 			PurchaseForm,
+			PsiPurchaseForm,
+			PsiWareHouseUpdateForm,
+			PsiLossForm,
+			PsiCollectForm,
 			HolidayForm,
 			DepartRegistrationAddForm,
 			HandoverAddForm,
@@ -234,4 +250,4 @@
 			}
 		}
 	}
-</script>
+</script>

+ 177 - 180
pages/workbench/task/TodoList.vue

@@ -1,209 +1,206 @@
 <template>
 	<view>
-		<cu-custom style="height: 10px;" :backUrl="'/pages/index/index'" :isBack="true" bgColor="bg-gradual-blue" >
+		<cu-custom style="height: 10px;" :backUrl="'/pages/index/index'" :isBack="true" bgColor="bg-gradual-blue">
 			<block slot="content">待办事项</block>
 		</cu-custom>
-		<u-search  :show-action="false" v-model="searchForm.title" @change="inputWord" margin="20rpx 50rpx"></u-search>
+		<u-search :show-action="false" v-model="searchForm.title" @change="inputWord" margin="20rpx 50rpx"></u-search>
 		<view>
 			<u-swipe-action>
-				<view
-					v-for="(row, index) in dataList"
-					:key="index">
-						<u-swipe-action-item @click="todo(row)"  :key="row.id" :threshold="60" duration="500" 
-						:options="[ {
-							text: '办理',
-							style: {
-								backgroundColor: '#3c9cff'
-							}
-						}]">
-						  <u-cell-group>
-							  <u-cell @click="todoCcpm(row)" v-if="row.belongProject === 'ccpm'">
-								  <view slot="title" class="content" >
-										<view class="text-bold text-grey">
-											<view class="ellipsis-description">
-												标题:{{row.vars.title}}
-											</view>
+				<view v-for="(row, index) in dataList" :key="index">
+					<u-swipe-action-item @click="todo(row)" :key="row.id" :threshold="60" duration="500" :options="[{
+						text: '办理',
+						style: {
+							backgroundColor: '#3c9cff'
+						}
+					}]">
+						<u-cell-group>
+							<u-cell @click="todoCcpm(row)" v-if="row.belongProject === 'ccpm'">
+								<view slot="title" class="content">
+									<view class="text-bold text-grey">
+										<view class="ellipsis-description">
+											标题:{{ row.vars.title }}
 										</view>
-										<view class="text-grey text-sm">
-											<view class="ellipsis-description">
-												 当前环节:{{row.task && row.task.name}}
-											</view>
+									</view>
+									<view class="text-grey text-sm">
+										<view class="ellipsis-description">
+											当前环节:{{ row.task && row.task.name }}
 										</view>
-										<view class="text-grey text-sm">
-											执行时间:{{row.task.createTime | formatDate}}
+									</view>
+									<view class="text-grey text-sm">
+										执行时间:{{ row.task.createTime | formatDate }}
+									</view>
+								</view>
+								<view slot="right-icon" class="action">
+									<u-tag text="等待审核" plain shape="circle" type="error"></u-tag>
+								</view>
+							</u-cell>
+							<u-cell @click="todo(row)" v-else>
+								<view slot="title" class="content">
+									<view class="text-bold text-grey">
+										<view class="ellipsis-description">
+											标题:{{ row.vars.title }}
 										</view>
-								  </view>
-								  <view slot="right-icon" class="action">
-									<u-tag  text="等待审核" plain shape="circle" type="error"></u-tag>
-								  </view>
-								</u-cell>
-							  <u-cell @click="todo(row)" v-else>
-								  <view slot="title" class="content" >
-									  <view class="text-bold text-grey">
-										  <view class="ellipsis-description">
-											  标题:{{row.vars.title}}
-										  </view>
-									  </view>
-									  <view class="text-grey text-sm">
-										  <view class="ellipsis-description">
-											  当前环节:{{row.task && row.task.name}}
-										  </view>
-									  </view>
-									  <view class="text-grey text-sm">
-										  执行时间:{{row.task.createTime | formatDate}}
-									  </view>
-								  </view>
-								  <view slot="right-icon" class="action">
-									  <u-tag  text="等待审核" plain shape="circle" type="error"></u-tag>
-								  </view>
-							  </u-cell>
-						  </u-cell-group>
-						</u-swipe-action-item>
+									</view>
+									<view class="text-grey text-sm">
+										<view class="ellipsis-description">
+											当前环节:{{ row.task && row.task.name }}
+										</view>
+									</view>
+									<view class="text-grey text-sm">
+										执行时间:{{ row.task.createTime | formatDate }}
+									</view>
+								</view>
+								<view slot="right-icon" class="action">
+									<u-tag text="等待审核" plain shape="circle" type="error"></u-tag>
+								</view>
+							</u-cell>
+						</u-cell-group>
+					</u-swipe-action-item>
 				</view>
 			</u-swipe-action>
 		</view>
-		<u-loadmore :status="status"  @loadmore="loadmore" :line="true" />
+		<u-loadmore :status="status" @loadmore="loadmore" :line="true" />
 		<u-gap height="20" bgColor="#fff"></u-gap>
 		<u-back-top :scrollTop="0" mode="square"></u-back-top>
 	</view>
 </template>
 
 <script>
-	import taskService from "@/api/flowable/taskService"
-	import userSelect from '@/components/user-select/user-select.vue'
-	import pick from 'lodash.pick'
-	export default {
-		components:{
-			userSelect
+import taskService from "@/api/flowable/taskService"
+import userSelect from '@/components/user-select/user-select.vue'
+import pick from 'lodash.pick'
+export default {
+	components: {
+		userSelect
+	},
+	data() {
+		return {
+			status: 'loadmore',
+			searchForm: {
+				title: ''
+			},
+			dataList: [],
+			tablePage: {
+				pages: 0,
+				currentPage: 0,
+				pageSize: 10,
+				orders: [{ column: "a.create_time", asc: false }],
+			},
+			loading: false,
+		}
+	},
+	onLoad() {
+		this.loadmore()
+	},
+	methods: {
+		// 跳转到详细页面
+		todo(row) {
+			taskService.getTaskDef({
+				taskId: row.task.id,
+				taskName: row.task.name,
+				taskDefKey: row.task.taskDefinitionKey,
+				procInsId: row.task.processInstanceId,
+				procDefId: row.task.processDefinitionId,
+				procDefKey: row.task.processDefKey,
+				status: row.status
+			}).then((data) => {
+				let query = { formTitle: `${row.vars.title}`, title: `审批【${row.task.name || ''}】`, ...pick(data, 'formType', 'formReadOnly', 'formUrl', 'procDefKey', 'taskDefKey', 'procInsId', 'procDefId', 'taskId', 'status', 'title', 'businessId') };
+				uni.navigateTo({
+					url: '/pages/workbench/task/TaskForm?flow=' + JSON.stringify(query)
+				})
+			})
 		},
-		data() {
-			return {
-				status: 'loadmore',
-				searchForm: {
-					title: ''
-				},
-				dataList: [],
-				tablePage: {
-					pages: 0,
-					currentPage: 0,
-					pageSize: 10,
-					orders: [{ column: "a.create_time", asc: false }],
-				},
-				loading: false,
+		// 审核方法
+		todoCcpm(row) {
+			if (row.task.processDefKey === '108' || row.task.processDefKey === '109' || row.task.processDefKey === '13' || row.task.processDefKey === '102') {
+				let id = row.vars.notifyId
+				let title = '报销申请审批'
+				this.toCenterForm(id, title, row.task.processDefKey, 'audit') // 跳转
+			} else if (row.task.processDefKey === '21' || row.task.processDefKey === '213') {
+
+				let id = row.vars.notifyId
+				let title = '发票申请审批'
+				this.toInvoiceUpdateCenterForm(id, title, row.task.processDefKey, 'audit') // 跳转
+			} else {
+				this.$message.warning('此流程的跨系统审核暂未开放,请前往所属系统进行审核')
 			}
 		},
-		onLoad() {
-			this.loadmore()
+		// 审核页面跳转
+		toCenterForm(id, title, processDefKey, type) {
+			// 生成参数对象
+			const query = {
+				id: id,
+				backPath: '/workbench/task/TodoList',
+				title: title,
+				processDefKey: processDefKey,
+				type: type
+			};
+
+			// 使用 uni.navigateTo 进行页面跳转
+			uni.navigateTo({
+				url: '/pages/generateForm/GenerateForm?flow=' + JSON.stringify(query)
+			});
 		},
-		methods: {
-			// 跳转到详细页面
-		      todo (row) {
-				  taskService.getTaskDef({
-					taskId: row.task.id,
-					taskName: row.task.name,
-					taskDefKey: row.task.taskDefinitionKey,
-					procInsId: row.task.processInstanceId,
-					procDefId: row.task.processDefinitionId,
-					procDefKey: row.task.processDefKey,
-					status: row.status
-				  }).then((data) => {
-					let query = {formTitle: `${row.vars.title}`, title: `审批【${row.task.name || ''}】`, ...pick(data, 'formType', 'formReadOnly', 'formUrl', 'procDefKey', 'taskDefKey', 'procInsId', 'procDefId', 'taskId', 'status', 'title', 'businessId')};
-					uni.navigateTo({
-					   url: '/pages/workbench/task/TaskForm?flow='+JSON.stringify(query)
-					})
-				  })
-		      },
-			// 审核方法
-			todoCcpm (row) {
-				if (row.task.processDefKey === '108' || row.task.processDefKey === '109' || row.task.processDefKey === '13'|| row.task.processDefKey === '102') {
-					let id = row.vars.notifyId
-					let title = '报销申请审批'
-					this.toCenterForm(id, title, row.task.processDefKey, 'audit') // 跳转
-				} else if (row.task.processDefKey === '21' || row.task.processDefKey === '213') {
+		// 发票审核页面跳转(财务审批)
+		toInvoiceUpdateCenterForm(id, title, processDefKey, type) {
+			// 生成参数对象
+			const query = {
+				id: id,
+				backPath: '/workbench/task/TodoList',
+				title: title,
+				processDefKey: processDefKey,
+				type: type
+			};
 
-					let id = row.vars.notifyId
-					let title = '发票申请审批'
-					this.toInvoiceUpdateCenterForm(id, title, row.task.processDefKey, 'audit') // 跳转
+			// 使用 uni.navigateTo 进行页面跳转
+			uni.navigateTo({
+				url: '/pages/generateForm/InvoiceUpdateGenerateForm?flow=' + JSON.stringify(query)
+			});
+		},
+		// 输入监听
+		inputWord(e) {
+			this.searchTimer && clearTimeout(this.searchTimer)
+			this.searchTimer = setTimeout(() => {
+				this.doSearch()
+			}, 300)
+		},
+		// 搜索
+		doSearch() {
+			this.dataList = [];
+			this.tablePage.currentPage = 0;
+			this.tablePage.pageSize = 10;
+			this.tablePage.pages = 0;
+			this.loadmore()
+		},
+		onReachBottom() {
+			this.loadmore()
+		},
+		loadmore() {
+			if (this.tablePage.currentPage !== 0 && this.tablePage.pages <= this.tablePage.currentPage) {
+				this.status = 'nomore';
+				return;
+			}
+			this.tablePage.currentPage = ++this.tablePage.currentPage;
+			//联网加载数据
+			this.status = 'loading';
+			taskService.todoList({
+				current: this.tablePage.currentPage,
+				size: this.tablePage.pageSize,
+				orders: this.tablePage.orders,
+				type: 'ydd',
+				...this.searchForm
+			}).then((data) => {
+				//追加新数据
+				this.dataList = this.dataList.concat(data.records);
+				this.tablePage.pages = data.pages;
+				if (this.tablePage.pages <= this.tablePage.currentPage) {
+					this.status = 'nomore'
 				} else {
-					this.$message.warning('此流程的跨系统审核暂未开放,请前往所属系统进行审核')
+					this.status = 'loadmore'
 				}
-			},
-			// 审核页面跳转
-			toCenterForm (id, title, processDefKey, type) {
-				// 生成参数对象
-				const query = {
-					id: id,
-					backPath: '/workbench/task/TodoList',
-					title: title,
-					processDefKey: processDefKey,
-					type: type
-				};
+			})
 
-				// 使用 uni.navigateTo 进行页面跳转
-				uni.navigateTo({
-					url: '/pages/generateForm/GenerateForm?flow=' + JSON.stringify(query)
-				});
-			},
-			// 发票审核页面跳转(财务审批)
-			toInvoiceUpdateCenterForm (id, title, processDefKey, type) {
-				// 生成参数对象
-				const query = {
-					id: id,
-					backPath: '/workbench/task/TodoList',
-					title: title,
-					processDefKey: processDefKey,
-					type: type
-				};
-
-				// 使用 uni.navigateTo 进行页面跳转
-				uni.navigateTo({
-					url: '/pages/generateForm/InvoiceUpdateGenerateForm?flow=' + JSON.stringify(query)
-				});
-			},
-			// 输入监听
-			inputWord(e){
-				this.searchTimer && clearTimeout(this.searchTimer)
-				this.searchTimer = setTimeout(()=>{
-					this.doSearch()
-				},300)
-			},
-			// 搜索
-			doSearch(){
-				this.dataList = []; 
-				this.tablePage.currentPage = 0;
-				this.tablePage.pageSize = 10;
-				this.tablePage.pages = 0;
-				this.loadmore()
-			},
-			onReachBottom() {
-				this.loadmore()
-			},
-			loadmore() {
-				if(this.tablePage.currentPage!==0 && this.tablePage.pages <= this.tablePage.currentPage ) {
-					this.status = 'nomore';
-					return;
-				}
-				this.tablePage.currentPage = ++ this.tablePage.currentPage;
-				//联网加载数据
-				this.status = 'loading';
-				taskService.todoList({
-					current: this.tablePage.currentPage,
-					size: this.tablePage.pageSize,
-					orders: this.tablePage.orders,
-					type: 'ydd',
-					...this.searchForm
-				}).then((data)=>{
-					//追加新数据
-					this.dataList=this.dataList.concat(data.records);
-					this.tablePage.pages = data.pages;
-					if(this.tablePage.pages <= this.tablePage.currentPage){
-						this.status = 'nomore'
-					} else {
-						this.status = 'loadmore'
-					}
-				})
-				
-			}
 		}
 	}
+}
 </script>

+ 16 - 5
pages/workbench/workbench.vue

@@ -114,7 +114,8 @@
 			};
 		},
 		computed: mapState({
-			 username: (state) => state.user.username
+			 username: (state) => state.user.username,
+			 userInfo: (state) => state.user.userInfo,
 		}),
 	    async mounted() {
 			
@@ -125,11 +126,16 @@
 			let list = data.records
 			let test = []
 			let leaveTest = []
+			let jjtTest = []
 			let resignation = []
 			let accounting = []
 			console.log('list', list)
 			list.forEach((item)=>{
 				if (item.category === '未设置') {
+					if (item.name === '进销存-领用申请') {
+						jjtTest.push(item)
+						// this.processMap.set('物资管理', [item])
+					}
 					if (item.name === '物资管理-领用申请') {
 						test.push(item)
 						// this.processMap.set('物资管理', [item])
@@ -170,10 +176,15 @@
 				}
 
 			})
-			this.processMap.set('物资管理', test)
-			/*this.processMap.set('日常办公', leaveTest)
-			this.processMap.set('人力资源管理', resignation)*/
-			this.processMap.set('会计', accounting)
+			if(this.userInfo.tenantDTO.id == "10009"){
+				this.processMap.set('景聚庭', jjtTest)
+			}else{
+				this.processMap.set('物资管理', test)
+				/*this.processMap.set('日常办公', leaveTest)
+				this.processMap.set('人力资源管理', resignation)*/
+				this.processMap.set('会计', accounting)
+			}
+		
 
 			// if (this.isAdmin) {
 			// 	//如果是管理员 查看所有的流程图信息