uni-app vue3 常用页面 组合式api方式

全局api请求封装 utils/request.js

import config from '@/utils/config';
// 统一 POST 请求方法示例
const post = (url, data, options = {}) => {
	url = config.url + url;
	console.log("uni.getStorageSync('token')==========", uni.getStorageSync('token'));
	const defaultOptions = {
		method: 'POST',
		header: {
			'Content-Type': 'application/json'
			// ,'token': `${uni.getStorageSync('token')}`
		}
	};
	const finalOptions = { ...defaultOptions, ...options };
	// 如果服务器需要 token,可以在这里添加 Authorization 头部
	if (!finalOptions.header['token'] && uni.getStorageSync('token')) {
		finalOptions.header['token'] = `${uni.getStorageSync('token')}`;
	}
	// if (!finalOptions.header['Authorization'] && uni.getStorageSync('token')) {
	//   finalOptions.header['Authorization'] = `Bearer ${uni.getStorageSync('token')}`;
	// }
	console.log('POST 请求参数=====', JSON.stringify(data));
	console.log('POST 请求header=====', finalOptions);
	console.log('POST url=====', url);

	return new Promise((resolve, reject) => {
		uni.request({
			...finalOptions,
			url: url,
			data: data,
			success: (res) => {
				console.log('请示结果=============', res);
				if (res.statusCode === 200) {
					resolve(res.data);
				} else {
					reject(new Error(`POST请求失败,状态码:${res.statusCode}`));
					uni.showToast({
						icon: 'none',
						title: `POST请求失败,状态码:${res.statusCode}`
					});
				}
			},
			fail: (err) => {
				reject(err);
				// 网络错误或其他请求失败的情况
				uni.showToast({
					icon: 'none',
					title: 'POST系统异常,请稍后再试'
				});
			}
		});
	});
};
// 统一 GET 请求方法示例
const get = (url, data, options = {}) => {
	url = config.url + url;
	console.log("uni.getStorageSync('token')==========", uni.getStorageSync('token'));
	const defaultOptions = {
		method: 'GET',
		header: {
			'Content-Type': 'application/json'
			// ,'token': `${uni.getStorageSync('token')}`
		}
	};
	const finalOptions = { ...defaultOptions, ...options };
	// 如果服务器需要 token,可以在这里添加 Authorization 头部
	if (!finalOptions.header['token'] && uni.getStorageSync('token')) {
		finalOptions.header['token'] = `${uni.getStorageSync('token')}`;
	}
	// if (!finalOptions.header['Authorization'] && uni.getStorageSync('token')) {
	//   finalOptions.header['Authorization'] = `Bearer ${uni.getStorageSync('token')}`;
	// }

	console.log('GET 请求header=====', finalOptions);
	console.log('GET url=====', url);
	console.log('GET 请求参数=====', data);
	return new Promise((resolve, reject) => {
		uni.request({
			...finalOptions,
			url: url,
			data: data,
			success: (res) => {
				console.log('GET请示结果=============', res);
				if (res.statusCode === 200) {
					resolve(res.data);
				} else {
					reject(new Error(`GET请求失败,状态码:${res.statusCode}`));
					uni.showToast({
						icon: 'none',
						title: `GET请求失败,状态码:${res.statusCode}`
					});
				}
			},
			fail: (err) => {
				reject(err);
				// 网络错误或其他请求失败的情况
				uni.showToast({
					icon: 'none',
					title: 'GET系统异常,请稍后再试'
				});
			}
		});
	});
};
export const request = {
	post,
	get
};

全局变量配置 main.js

import App from './App';
import store from './store';
import request from '@/http/request.js';

// #ifndef VUE3
import Vue from 'vue';
Vue.config.productionTip = false;

Vue.prototype.$store = store;
Vue.prototype.$adpid = '1111111111';
Vue.prototype.$backgroundAudioData = {
	playing: false,
	playTime: 0,
	formatedPlayTime: '00:00:00'
};
Vue.prototype.$request = request;

// 设置全局变量和函数(Vue 2)
Vue.prototype.$globalData = null;
Vue.prototype.$setGlobalData = (data) => {
  Vue.prototype.$globalData = data;
};
Vue.prototype.$getGlobalData = () => {
  return Vue.prototype.$globalData;
};

Vue.prototype.$globalData2 = null;
Vue.prototype.$setGlobalData2 = (data) => {
  Vue.prototype.$globalData2 = data;
};
Vue.prototype.$getGlobalData2 = () => {
  return Vue.prototype.$globalData2;
};

Vue.prototype.$globalData3 = null;
Vue.prototype.$setGlobalData3 = (data) => {
  Vue.prototype.$globalData3 = data;
};
Vue.prototype.$getGlobalData3 = () => {
  return Vue.prototype.$globalData3;
};


App.mpType = 'app';
const app = new Vue({
	store,
	...App
}); 
app.$mount();
// #endif

// #ifdef VUE3
import { createSSRApp } from 'vue';

export function createApp() {
	const app = createSSRApp(App);
	app.use(store);
	
	app.config.globalProperties.$adpid = '1111111111';
	app.config.globalProperties.$backgroundAudioData = {
		playing: false,
		playTime: 0,
		formatedPlayTime: '00:00:00'
	};
	app.config.globalProperties.$request = request;
	
	 // 注意:在 Vue 3 中,全局变量和函数应该直接设置在 app.config.globalProperties 上,
	  // 而不是像 Vue 2 那样通过 Vue.prototype。但是,为了保持一致性,并且能够在组件中
	  // 通过 this.$xxx 的方式访问,我们仍然可以在这里设置它们。
	  // 不过,通常建议通过组合式 API 的 getCurrentInstance 来访问这些全局属性。
	  app.config.globalProperties.$globalData = null;
	  app.config.globalProperties.$setGlobalData = (data) => {
	    app.config.globalProperties.$globalData = data;
	  };
	  app.config.globalProperties.$getGlobalData = () => {
	    return app.config.globalProperties.$globalData;
	  };
	  
	  app.config.globalProperties.$globalData2 = null;
	  app.config.globalProperties.$setGlobalData2 = (data) => {
	    app.config.globalProperties.$globalData2 = data;
	  };
	  app.config.globalProperties.$getGlobalData2 = () => {
	    return app.config.globalProperties.$globalData2;
	  };
	  
	  app.config.globalProperties.$globalData3 = null;
	  app.config.globalProperties.$setGlobalData3 = (data) => {
	    app.config.globalProperties.$globalData3 = data;
	  };
	  app.config.globalProperties.$getGlobalData3 = () => {
	    return app.config.globalProperties.$globalData3;
	  };
	  
	  
	return {
		app
	};
}
// #endif

页面级api封装

 user.ts

import { request } from '@/utils/request.js';
//1.登录得到code
const LoginByOauth = (data) => {
	return request.post('/levault/usrsvr/Usr/LoginByOauth', data);
};
//2.通过code获取Token
const GetTokenFromCode = (data) => {
	return request.post('/levault/usrsvr/Usr/GetTokenFromCode', data);
};
export const userApi = {
	LoginByOauth,
	GetTokenFromCode
};

receiptList.ts 

import { request } from '@/utils/request.js';
import config from '@/utils/config';

//收货单分页查询
export const queryPage = (data) => {
	return request.post(config.baseUrl + '/ReceiveOrder/queryPage', data);
};
//收货单行查询
export const receiveJobTaskSeach = (data) => {
	return request.post(config.baseUrl + '/Tj0ReceiveJobTask/receiveJobTaskSeach', data);
};
//收货异常数量提报
export const receiveAbnormal = (data) => {
	return request.post(config.baseUrl + '/Tj0ReceiveJobTask/receiveAbnormal', data);
};
//收货单提交
export const receiveSubmit = (data) => {
	return request.post(config.baseUrl + '/Tj0ReceiveJobTask/receiveSubmit', data);
};

vue页面

登录页面vue

<template>
	<view>
		<view class="uni-common-mt">
			<view class="uni-flex uni-column">
				<view class="flex-item  flex-item-V">
					<view class="logo">
						<image src="/static/image/uniui-logo.png" style="height:90px;display: none;"></image>
					</view>
					<view class="logoHead">欢迎使用WMS</view>
				</view>
				<view class="flex-item flex-item-V">

				</view>
				<view class="flex-item flex-item-V form">
					<form ref="myForm" @submit="formSubmit">
						<view class="uni-form-item uni-column">
							<uni-easyinput name="loginName" placeholder="请输入账号" prefixIcon="person" value="">
							</uni-easyinput>
						</view>

						<view class="uni-form-item uni-column">
							<uni-easyinput name="password" type="password" placeholder="请输入密码" prefixIcon="locked"
								value="" @confirm="handlePasswordConfirm ">
							</uni-easyinput>
						</view>

						<view class="uni-btn-v loginBtn">
							<button form-type="submit" type="primary" :loading="loginLoading">登 录</button>

						</view>
					</form>
				</view>
			</view>
		</view>
	</view>
</template>

<script setup>
	import {
		nextTick,
		onMounted,
		ref
	} from 'vue';
	import {
		userApi
	} from "@/api/user";
	import graceChecker from "@/common/graceChecker.js";
	import CryptoJS from 'crypto-js';
	import G from '@/utils/global.js'

	const loginLoading = ref(false);
	const myForm = ref(); 
	const handlePasswordConfirm = () => {
	  // myForm.value.submit();
	};
	const formSubmit = (e) => {
		console.log('form发生了submit事件,携带数据为:' + JSON.stringify(e.detail.value))
		//定义表单规则
		var rule = [{
				name: "loginName",
				checkType: "string",
				checkRule: "1,10",
				errorMsg: "请输入1-10位的账号"
			},
			{
				name: "password",
				checkType: "string",
				checkRule: "3,15",
				errorMsg: "请输入3-15密码"
			}
		];
		//进行表单检查
		var formData = e.detail.value;
		var checkRes = graceChecker.check(formData, rule);
		if (checkRes) {
			//登录信息
			formData.tenantId = "1867144130501541888";
			formData.lang = "zh_cn";
			formData.industry = null;
			formData.password = CryptoJS.SHA256(formData.password).toString(CryptoJS.enc.Hex);
			let thisObj = {
				"thisObj": formData
			};
			loginLoading.value = true;
			uni.setStorageSync('token', null);

			//1.登录得到code
			userApi.LoginByOauth(thisObj).then(loginRes => {
				console.log('loginRes========', JSON.stringify(loginRes));
				if (loginRes.mfail != '0' || loginRes.data == null) {
					loginLoading.value = false;
					uni.showToast({
						icon: 'none',
						duration: G.ShowPopTime,
						title: `登录异常,code获取异常:${loginRes.msg}`
					});
					return;
				} else {
					//2.通过code获取Token
					let postData = {
						code: loginRes.data.code
					};
					//获取token
					userApi.GetTokenFromCode(postData).then(tokenRes => {
						console.log('Token========', JSON.stringify(tokenRes));
						if (tokenRes.mfail != '0' || tokenRes.data == null) {
							loginLoading.value = false;
							uni.showToast({
								icon: 'none',
								duration: G.ShowPopTime,
								title: `登录异常,token获取异常:${res.msg}`
							});
							return;
						}
						//登录成功后,将token写入全局变量
						uni.setStorageSync('token', tokenRes.data.token);
						//跳转到首页
						nextTick(() => {
							uni.reLaunch({
								animationType: 'zoom-out',
								animationDuration: 200,
								url: "/pages/index/index"
							});

						});
					}).catch(error => {
						uni.setStorageSync('token', null);
						loginLoading.value = false;
						uni.showToast({
							icon: 'none',
							duration: G.ShowPopTime,
							title: `访问失败,请联系管理员!:${res.msg}`
						});

					});

				}

			}).catch(error => {
				uni.setStorageSync('token', null);
				loginLoading.value = false;
				uni.showToast({
					icon: 'none',
					duration: G.ShowPopTime,
					title: `访问失败,请联系管理员!:${res.msg}`
				});
			});

		} else {
			loginLoading.value = false;
			uni.showToast({
				title: graceChecker.error,
				icon: "none",
				duration: G.ShowPopTime
			});
		}

	}
	onMounted(() => {
		//uni.setStorageSync('token', null);

	});
</script>

<style scoped lang="scss">
	view {
		box-sizing: border-box;

	}

	.uni-common-mt {
		margin: 0;
	}

	.flex-item:nth-child(1) {
		display: flex;
		flex-flow: column;
		justify-content: center;
		align-content: center;
	}

	.flex-item:nth-child(2) {
		height: 120rpx;
		background: url("/static/login-style.png") round space;

	}

	.logo {
		min-height: 100rpx;
		padding-left: 10px;
		margin-top: 100rpx;
	}

	.logoHead {
		font-family: 'Microsoft YaHei', sans-serif;
		text-align: center;
		color: darkblue;
		font-size: 58rpx;
		font-weight: bold;
		text-shadow: 1px 1px rgba(0, 0, 0, .3);
		padding-bottom: 40rpx;

	}

	.loginBtn {
		padding-top: 40rpx;
	}

	.form {
		padding: 0px 40px;
	}
</style>

主页面VUE

<template>
	<view class="container">
		<uni-nav-bar dark :fixed="true" shadow background-color="#007AFF" status-bar left-icon="left" right-icon="home" left-text="退出"
			title="主页面" @clickLeft="back" />

		<uni-section title="菜单" type="line" padding>
			<uni-grid :column="3" :show-border="false" :square="false" :highlight="true" @change="gotoPage">
				<uni-grid-item v-for="(item ,index) in list" :index="index" :key="index">
					<view class="grid-item-box">
						<image :src="item.imgUrl" class="image" mode="aspectFill" />
						<!-- <uni-icons type="image" size="64" color="#0573f9"></uni-icons> -->
						<text class="text">{
  
  { item.text }}</text>
					</view>
				</uni-grid-item>
			</uni-grid>
		</uni-section>

	</view>
	<view>
		<!-- 提示窗示例 -->
		<uni-popup ref="alertDialog" type="dialog">
			<uni-popup-dialog :type="msgType" cancelText="关闭" confirmText="确定" title="提示" content="确定退出登录吗?"
				@confirm="dialogConfirm"></uni-popup-dialog>
		</uni-popup>
	</view>
</template>

<script setup>
	import {
		nextTick,
		onMounted,
		reactive,
		ref
	} from 'vue';
	const alertDialog = ref();
	const msgType = ref("error");
	const props = reactive({
		hasLeftWin: {
			type: Boolean
		},
		leftWinActive: {
			type: String
		}
	});
	const goto = (url) => {
		uni.navigateTo({
			url: url
		})
	};
	const dynamicList = reactive([]);
	// const dynamicList = reactive([]);
	const list = reactive([{
			url: '/pages/receiptConfirm/index',
			imgUrl: '/static/image/cart2.png',
			text: '收货确认',
			badge: '0',
			type: "primary"
		},
		{
			url: '/pages/receiptList/index',
			imgUrl: '/static/image/cart3.png',
			text: '收货任务',
			badge: '1',
			type: "success"
		},
		{
			url: '/pages/stocktaking/index',
			imgUrl: '/static/image/cart2.png',
			text: '盘点',
			badge: '99',
			type: "warning"
		},
		{
			url: '/pages/intelligentLibraryTray/index',
			imgUrl: '/static/image/cart3.png',
			text: '智能库托盘',
			badge: '2',
			type: "error"
		},
		{
			url: '/pages/loadingTask/index',
			imgUrl: '/static/image/cart2.png',
			text: '上架任务',
			badge: '2',
			type: "error"
		},
		{
			url: '/pages/inventoryQuery/index',
			imgUrl: '/static/image/cart3.png',
			text: '库存查询',
			badge: '2',
			type: "error"
		},
		{
			url: '/pages/pickingTask/index',
			imgUrl: '/static/image/cart2.png',
			text: '拣货任务',
			badge: '2',
			type: "error"
		},
		{
			url: '/pages/unpacking/index',
			imgUrl: '/static/image/cart3.png',
			text: '拆包'
		},
		{
			url: '/pages/reprintRawMaterial/index',
			imgUrl: '/static/image/cart2.png',
			text: '原材料补打'
		},
		{
			url: '/pages/moveInventory/index',
			imgUrl: '/static/image/cart3.png',
			text: '移库'
		},
		{
			url: '/pages/MESMaterialDistribution/index',
			imgUrl: '/static/image/cart2.png',
			text: '物料配送'
		}
	]);

	const gotoPage = (e) => {
		let {
			index
		} = e.detail;
		console.log(e.detail, list[index].url);
		if (list[index]) { //页面跳转
			nextTick(() => {
				uni.navigateTo({
					url: list[index].url,
					animationType: 'zoom-out',
					animationDuration: 200
				});
			});
			// uni.showToast({
			// 	title: `点击第${index + 1}个宫格`,
			// 	icon: 'none'
			// })
		}
	};
	//退出系统
	const dialogConfirm = () => {
		uni.reLaunch({
			animationType: 'zoom-out',
			animationDuration: 200,
			url: "/pages/login/index"
		});

	};
	//退出系统事件
	const back = () => {
		msgType.value = "error";
		alertDialog.value.open();
	};
	// const add = () => {
	// 	if (dynamicList.length < 9) {
	// 		dynamicList.push({
	// 			url: `/static/c${dynamicList.length + 1}.png`,
	// 			text: `Grid ${dynamicList.length + 1}`,
	// 			color: dynamicList.length % 2 === 0 ? '#f5f5f5' : "#fff"
	// 		})
	// 	} else {
	// 		uni.showToast({
	// 			title: '最多添加9个',
	// 			icon: 'none'
	// 		});
	// 	}
	// };
	// const del = () => {
	// 	dynamicList.splice(dynamicList.length - 1, 1)
	// }; 

	onMounted(() => {

	});
</script>
<script>
	export default {

		onNavigationBarButtonTap(e) {
			if (e.index === 0) {
				//dialogToggle("error");
				this.msgType = "error";
				this.$refs.alertDialog.open();
			}
		}
	}
</script>
<style scoped lang="scss">
	view {
		box-sizing: border-box;
	}

	.image {
		width: 48px;
		height: 48px;
	}

	.text {
		font-size: 14px;
		margin-top: 5px;
	}

	.example-body {
		/* #ifndef APP-NVUE */
		// display: block;
		/* #endif */
	}

	.grid-dynamic-box {
		margin-bottom: 15px;
	}

	.grid-item-box {
		flex: 1;
		// position: relative;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: column;
		align-items: center;
		justify-content: center;
		padding: 15px 0;
	}

	.grid-item-box-row {
		flex: 1;
		// position: relative;
		/* #ifndef APP-NVUE */
		display: flex;
		/* #endif */
		flex-direction: row;
		align-items: center;
		justify-content: center;
		padding: 15px 0;
	}

	.grid-dot {
		position: absolute;
		top: 5px;
		right: 15px;
	}

	.swiper {
		height: 420px;
	}

	/* #ifdef H5 */
	@media screen and (min-width: 768px) and (max-width: 1425px) {
		.swiper {
			height: 630px;
		}
	}

	@media screen and (min-width: 1425px) {
		.swiper {
			height: 830px;
		}
	}

	/* #endif */
</style>

receiptList/index.vue

<template>
	<z-paging ref="paging" v-model="dataList" @query="queryList">
		<template #top>
			<uni-group class="searchForm">
				<uni-easyinput v-model="orderNumber" placeholder="请输入或扫描送货单号" prefixIcon="scan" @confirm="onInput">
				</uni-easyinput>

			</uni-group>
			<view style="margin: 0 5px;">

				<uni-segmented-control :current="current" :values="items" @clickItem="onClickItem" styleType="button">

				</uni-segmented-control>
			</view>

		</template>

		<view class="content">

			<view class="itemCard" v-for="(item,index) in dataList" :key="index" @click="itemClick(item)">
				<view class="item">
					<view class="example-body">
						<uni-row class="demo-uni-row">
							<uni-col :span="24">
								<view class="listItme">
									<view class="listTitle blueBar">送货单号</view>
									<view class="listContent">{
  
  {item.orderNumber}}</view>
								</view>
							</uni-col>
							<uni-col :span="24">
								<view class="listItme">
									<view class="listTitle">关联单号</view>
									<view>{
  
  {item.relatedOrderNumber}}</view>
								</view>
							</uni-col>
							<uni-col :span="24">
								<view class="listItme">
									<view class="listTitle">单据来源</view>
									<view class="listContent">{
  
  {item.orderSource}}</view>
								</view>
							</uni-col>
							<uni-col :span="24">
								<view class="listItme">
									<view class="listTitle">开始时间</view>
									<view class="listContent">{
  
  {item.planStartTime}}</view>
								</view>
							</uni-col>
							<uni-col :span="24">
								<view class="listItme">
									<view class="listTitle">结束时间</view>
									<view class="listContent">{
  
  {item.planEndTime}}</view>
								</view>
							</uni-col>
							<uni-col :span="24">
								<view class="listItme">
									<view class="listTitle">收货人</view>
									<view class="listContent">{
  
  {item.responsiblePerson}}</view>
								</view>
							</uni-col>
						</uni-row>

					</view>
					<view class="item-arrow">
						<uni-icons type="right" size="25" color="grey"></uni-icons>
					</view>
				</view>
			</view>



		</view>
	</z-paging>


</template>
<script setup>
	import {
		onMounted,
		reactive,
		ref,
		getCurrentInstance
	} from 'vue';
	import {
		queryPage
	} from "@/api/receiptList";
	const instance = getCurrentInstance();
	const paging = ref(null);
	const focus = reactive(true);
	const dataList = ref([]);

	const items = ['收货中', '待收货', '已收货'];
	const current = ref(0); // 当前选中的选项卡,默认为第一个选项卡

	const orderNumber = ref("");
	//查询
	const onInput = (e) => {
		paging.value.reload();
	};
	const onClickItem = (e) => {
		current.value = e.currentIndex; // 获取当前选中的索引
		paging.value.reload();
	};
	// @query所绑定的方法不要自己调用!!需要刷新列表数据时,只需要调用paging.value.reload()即可
	const queryList = (pageNo, pageSize) => {
		// 组件加载时会自动触发此方法,因此默认页面加载时会自动触发,无需手动调用
		// 这里的pageNo和pageSize会自动计算好,直接传给服务器即可
		// 模拟请求服务器获取分页数据,请替换成自己的网络请求
		const params = {
			"attrSet": [],
			"condition": [],
			"sorts": [{
				"name": "createAt",
				"sort": "desc"
			}],
			"page": {
				"pageNo": pageNo,
				"pageSize": pageSize
			}
		};

		if (orderNumber.value != '') {
			params.condition.push({
				"compare": "LIKE",
				"field": "orderNumber",
				"value": orderNumber.value
			});
		}
		if (current.value == 0) {
			//收货中
			params.condition.push({
				"compare": "EQUAL",
				"field": "orderStatus",
				"value": 'Created'
			});
		} else if (current.value == 1) {
			//待收货
			params.condition.push({
				"compare": "EQUAL",
				"field": "orderStatus",
				"value": 'SH'
			});
		}
		if (current.value == 2) {
			//已收货
			params.condition.push({
				"compare": "EQUAL",
				"field": "orderStatus",
				"value": 'SHWC'
			});
		}
		queryPage(params).then(res => {
			console.log("============", JSON.stringify(res));
			if (res.code == 0) {
				// 将请求的结果数组传递给z-paging
				paging.value.complete(res.data);
			} else { //异常信息
				paging.value.complete(false);

				uni.showToast({
					title: res.msg
				});
			}

		}).catch(res => {
			// 如果请求失败写paging.value.complete(false);
			// 注意,每次都需要在catch中写这句话很麻烦,z-paging提供了方案可以全局统一处理
			// 在底层的网络请求抛出异常时,写uni.$emit('z-paging-error-emit');即可
			paging.value.complete(false);
		});

	};

	const itemClick = (item) => {
		instance.appContext.config.globalProperties.$setGlobalData(item);
		uni.navigateTo({
			url: '/pages/receiptList/confirm'
		});

		// let infoData = JSON.stringify(item)
		// let newStr = encodeURIComponent(infoData);
		// uni.navigateTo({
		// 	url: '/pages/receiptList/confirm?data=' + newStr
		// });
		// console.log('点击了', item);
	};
	onMounted(() => {

		console.log("加載成功。。。");
	});
</script>

<style scoped lang="scss">
	view {
		box-sizing: border-box;
		color: $uni-text-color;
	}

	button {
		font-size: $uni-btn-font-size !important;
	}

	.searchForm {
		margin-top: 0 !important;
	}

	.content {
		margin: 0;
	}

	.itemCard {
		padding: 10rpx 10rpx 10rpx 15rpx;
		z-index: 1;
		border: 1px solid #ddd;
		border-radius: 6px;
		background-color: $uni-bg-color;
		margin: 8rpx 8rpx 0px 8rpx !important;

	}

	.item {
		z-index: 1;
		position: relative;

		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 0;
	}



	.item-line {
		position: absolute;
		bottom: 0rpx;
		left: 0rpx;
		height: 1px;
	}

	.listItme {
		display: flex;
		justify-content: row;
		line-height: 55rpx;
	}


	.listTitle {
		padding-left: 20rpx;
		font-weight: bold;
		min-width: 120rpx;
		padding-right: 10rpx;
		white-space: nowrap;
		/* 禁止换行 */
		overflow: hidden;
	}

	.listTitle::after {
		white-space: pre;
		content: " ";
	}

	.listContent {
		min-width: 120rpx;
		margin-right: 10rpx;
		white-space: nowrap;
		/* 禁止换行 */
		overflow: hidden;
		/* 隐藏溢出内容 */
		text-overflow: ellipsis;
		/* 使用省略号表示溢出 */
	}

	//蓝色条块
	.blueBar {
		margin-left: -22rpx;
	}

	.blueBar::before {
		white-space: pre;
		content: " ";
		background-color: $uni-border-bar-color; // $uni-color-primary ;
		width: 4px;
		/* 竖块的宽度 */
		height: 12px;
		border-radius: 10px;
		margin-right: 15rpx;
	}
</style>

receiptList/confirm.vue

<template>
	<z-paging ref="paging" v-model="dataList" @query="queryList">
		<!-- 需要固定在顶部不滚动的view放在slot="top"的view中,如果需要跟着滚动,则不要设置slot="top" -->
		<!-- 注意!此处的z-tabs为独立的组件,可替换为第三方的tabs,若需要使用z-tabs,请在插件市场搜索z-tabs并引入,否则会报插件找不到的错误 -->
		<template #top>
			<view class="searchForm">
				<view>
					<uni-easyinput name="userName" placeholder="请扫描物料二维码" prefixIcon="scan" @confirm="onInput">
					</uni-easyinput>
				</view>

				<view class="example-body searchFormList">
					<uni-row class="demo-uni-row">
						<uni-col :span="15">
							<view class="listItme">
								<view class="listTitle">送货单号</view>
								<view class="listContent">{
  
  {selectItem.orderNumber}}</view>
							</view>
						</uni-col>
						<uni-col :span="9">
							<view class="listItme">
								<view class="listTitle">总件数</view>
								<view class="listContent">2000</view>
							</view>
						</uni-col>
						<uni-col :span="15">
							<view class="listItme">
								<view class="listTitle">已收数量</view>
								<view class="listContent">5000</view>
							</view>
						</uni-col>
						<uni-col :span="9">
							<view class="listItme">
								<view class="listTitle">待收数量</view>
								<view class="listContent">200</view>
							</view>
						</uni-col>
					</uni-row>

				</view>
				<view class="btnList2">
					<view>
						<button type="primary" :loading="loginLoading" @click="submitOrder">提 交</button>
					</view>
					<view>
						<button type="primary" plain="true">条码补打</button>
					</view>
				</view>

			</view>
		</template>

		<!-- 如果希望其他view跟着页面滚动,可以放在z-paging标签内 -->
		<view>
			<view class="itemCard" v-for="(item,index) in dataList" :key="index">
				<view class="example-body">
					<uni-row class="demo-uni-row">
						<uni-col :span="24">
							<view class="listItme">
								<view class="listTitle blueBar">物料号</view>
								<view class="listContent">{
  
  {item.materialDepict}} {
  
  {item.materialCode}}</view>
							</view>
						</uni-col>

						<uni-col :span="10">
							<view class="listItme">
								<view class="listTitle">箱号</view>
								<view class="listContent">{
  
  {item.consumablesCode}}</view>
							</view>
						</uni-col>

						<uni-col :span="14">
							<view class="listItme">
								<view class="listTitle">是否检验</view>
								<view class="listContent">{
  
  {item.qualityInspection}}</view>
							</view>
						</uni-col>
						<uni-col :span="10">
							<view class="listItme">
								<view class="listTitle">收货状态</view>
								<view class="listContent">{
  
  {item.taskStatus}}</view>
							</view>
						</uni-col>

						<uni-col :span="14">
							<view class="listItme">
								<view class="listTitle">送货数量</view>
								<view class="listContent">{
  
  {item.taskQuantity}}</view>
							</view>
						</uni-col>
						<uni-col :span="10">
							<view class="listItme">
								<view class="listTitle">清点数量</view>
								<view class="listContent">

									<input class="uniInput" type="number" maxlength="10" placeholder="输入数量"
										v-model="item.actualOperateQuantity" />
								</view>
							</view>
						</uni-col>

						<uni-col :span="14">
							<view class="listItme">
								<view class="listTitle">智库物料</view>
								<view class="listContent">{
  
  {item.materialCode}}</view>
							</view>
						</uni-col>
						<uni-col :span="10">
							<view class="listItme">
								<view class="listTitle">行号</view>
								<view class="listContent">{
  
  {item.tj0receiveLineNumber}}</view>
							</view>
						</uni-col>
						<uni-col :span="10">
							<view class="listItme">
								<view class="listContent2">
									<button type="warn" @click="toggle(item)" size="mini">收货异常</button>
								</view>
							</view>
						</uni-col>


					</uni-row>

				</view>
				<view>
					<uni-collapse accordion>
						<uni-collapse-item title="物料明细">
							<view class="collapseBody">
								<uni-row class="demo-uni-row" v-for="(citem,index) in item.serialNumberOutVOList"
									:key="index">
									<uni-col :span="24">
										<view class="item_line"></view>
									</uni-col>

									<uni-col :span="24">
										<view class="listItme">
											<view class="listTitle">SN/序列号</view>
											<view class="listContent">{
  
  {citem.serialNum}}</view>
										</view>
									</uni-col>
									<uni-col :span="24">
										<view class="listItme">
											<view class="listTitle">批次号</view>
											<view class="listContent">{
  
  {citem.batchNumber}}</view>
										</view>
									</uni-col>
									<uni-col :span="12">
										<view class="listItme">
											<view class="listTitle">数 量</view>
											<view class="listContent">{
  
  {citem.actualOperateQuantity}}</view>
										</view>
									</uni-col>


								</uni-row>

							</view>
						</uni-collapse-item>

					</uni-collapse>
				</view>
			</view>

		</view>

	</z-paging>
	<view>
		<!-- 普通弹窗 -->
		<uni-popup ref="refPopup" background-color="#fff" @change="change">
			<view class="popup-content">
				<uni-section title="收货异常" type="line">
					<view class="example">
						<!-- 基础表单校验 -->
						<uni-forms ref="formRef" :rules="customRules" :model="valiFormData" labelWidth="80px">
							<uni-forms-item label="物料编码" name="materialCode">
								<uni-easyinput disabled v-model="valiFormData.materialCode" placeholder="" />
							</uni-forms-item>
							<uni-forms-item label=" 送货数量" name="taskQuantity">
								<uni-easyinput type="number" disabled v-model="valiFormData.taskQuantity"
									placeholder="" />
							</uni-forms-item>
							<uni-forms-item label="清点数量" required name="actualOperateQuantity">
								<uni-easyinput type="number" v-model="valiFormData.actualOperateQuantity" maxlength="10"
									placeholder="输入数量" />
							</uni-forms-item>
							<uni-forms-item label="损坏数量" required name="tj0damagedQuantity">
								<uni-easyinput type="number" v-model="valiFormData.tj0damagedQuantity" maxlength="10"
									placeholder="输入数量" />
							</uni-forms-item>
							<uni-forms-item label="破损数量" required name="tj0wornQuantity">
								<uni-easyinput type="number" v-model="valiFormData.tj0wornQuantity" maxlength="10"
									placeholder="输入数量" />
							</uni-forms-item>

						</uni-forms>
						<button type="primary" @click="submit('valiForm')">保 存</button>
					</view>
				</uni-section>
			</view>
		</uni-popup>
	</view>
</template>
<script setup>
	import {
		onMounted,
		reactive,
		ref,
		getCurrentInstance
	} from 'vue';
	import {
		receiveJobTaskSeach,
		receiveAbnormal,
		receiveSubmit
	} from "@/api/receiptList";
	const instance = getCurrentInstance();

	const loginLoading = ref(false);
	const formRef = ref();
	const baseFormData = reactive({});
	const selectItem = ref({});

	const popupType = reactive("bottom");
	const refPopup = ref();
	var itemValue = {};
	//提交
	const submitOrder = () => {
		let params = selectItem.value;
		params.taskList = dataList.value;
		console.log("params============", JSON.stringify(params));
		loginLoading.value = true;
		receiveSubmit(params).then(res => {
			loginLoading.value = false;
			if (res.code == 0) {
				refPopup.value.close();
				uni.showToast({
					title: `保存成功`,
					icon: 'success',
					duration: 3000
				});
			} else { //异常信息
				uni.showToast({
					title: res.msg
				});
			}

		}).catch(res => {
			loginLoading.value = false;
			paging.value.complete(false);
		});
	};
	const valiFormData = reactive({});
	// 自定义表单校验规则
	const customRules = reactive({
		actualOperateQuantity: {
			rules: [{
				required: true,
				errorMessage: '清点数量,不能为空'
			}]
		},
		tj0damagedQuantity: {
			rules: [{
				required: true,
				errorMessage: '损坏数量,不能为空'
			}]
		},
		tj0wornQuantity: {
			rules: [{
				required: true,
				errorMessage: '破损数量,不能为空'
			}]
		}

	});
	const paging = ref(null);
	const dataList = ref([]);

	// @query所绑定的方法不要自己调用!!需要刷新列表数据时,只需要调用paging.value.reload()即可
	const queryList = (pageNo, pageSize) => {
		// 组件加载时会自动触发此方法,因此默认页面加载时会自动触发,无需手动调用
		// 这里的pageNo和pageSize会自动计算好,直接传给服务器即可
		// 模拟请求服务器获取分页数据,请替换成自己的网络请求
		const params = {
			"attrSet": [],
			"condition": [],
			"sorts": [{
				"name": "createAt",
				"sort": "desc"
			}],
			"page": {
				"pageNo": pageNo,
				"pageSize": pageSize
			}
		};


		params.condition.push({
			"compare": "EQUAL",
			"field": "mainId",
			"value": selectItem.value.objId
		});
		receiveJobTaskSeach(params).then(res => {
			console.log("============", JSON.stringify(res));
			if (res.code == 0) {
				// 将请求的结果数组传递给z-paging
				res.data.forEach(i => {
					if (i.taskQuantity != null && i.taskQuantity != "" && i.taskQuantity !=
						0)
						i.actualOperateQuantity = i.taskQuantity;
				});
				paging.value.complete(res.data);
			} else { //异常信息

				paging.value.complete(false);

				uni.showToast({
					title: res.msg
				});
			}
		}).catch(res => {
			// 如果请求失败写paging.value.complete(false);
			// 注意,每次都需要在catch中写这句话很麻烦,z-paging提供了方案可以全局统一处理
			// 在底层的网络请求抛出异常时,写uni.$emit('z-paging-error-emit');即可
			paging.value.complete(false);

		});

	};
	const itemClick = (item) => {
		let infoData = JSON.stringify(item)
		let newStr = infoData.replace(/%/g, '%25');
		uni.navigateTo({
			url: '/pages/receiptConfirm/confirm?data=' + encodeURIComponent(newStr)
		})
		// console.log('点击了', item);
	};
	//收货异常
	const toggle = (item) => {
		itemValue = item;
		valiFormData.objId = item.objId;
		valiFormData.materialCode = item.materialCode;
		valiFormData.taskQuantity = item.taskQuantity;

		valiFormData.actualOperateQuantity = item.actualOperateQuantity;
		valiFormData.tj0damagedQuantity = item.tj0damagedQuantity;
		valiFormData.tj0wornQuantity = item.tj0wornQuantity;
		// open 方法传入参数 等同在 uni-popup 组件上绑定 type属性
		refPopup.value.open(popupType);
		//console.log('item', JSON.stringify(item));
	};
	//收货异常提交
	const submit = async (ref) => {
		console.log("baseFormData====", JSON.stringify(baseFormData));
		await formRef?.value?.validate();
		// formRef.value.validate().then(res => {
		// 	loginLoading.value = true;
		// 	console.log('success', res);
		// 	uni.showToast({
		// 		title: `校验通过`
		// 	})
		// }).catch(err => {
		// 	console.log('err', err);
		// });
		itemValue.actualOperateQuantity = valiFormData.actualOperateQuantity;
		itemValue.tj0damagedQuantity = valiFormData.tj0damagedQuantity;
		itemValue.tj0wornQuantity = valiFormData.tj0wornQuantity;
		refPopup.value.close();
		uni.showToast({
			title: `保存成功`,
			icon: 'success',
			duration: 2000
		});
		/*
		receiveAbnormal(valiFormData).then(res => {
			loginLoading.value = false;
			console.log("============", JSON.stringify(res));
			
		
			if (res.code == 0) {
				refPopup.value.close();
				valiFormData.actualOperateQuantity = "";
				valiFormData.tj0damagedQuantity = "";
				valiFormData.tj0wornQuantity = "";
				
				uni.showToast({
					title: `保存成功`,
					icon: 'success',
					duration: 3000
				});
			} else { //异常信息
				uni.showToast({
					title: res.msg
				});
			}

		}).catch(res => {
			loginLoading.value = false;
			paging.value.complete(false);
		});*/

	};
	onMounted(() => {
		const pages = getCurrentPages();
		const currentPage = pages[pages.length - 1]; // 当前页面
		//取全局变量参数
		const globalData = instance.appContext.config.globalProperties.$getGlobalData();
		selectItem.value = globalData;

		// //父页面data参数
		// if (currentPage.options && currentPage.options.data) {
		// 	let infoData = decodeURIComponent(currentPage.options.data);
		// 	//	console.log("infoData----------", JSON.stringify(infoData))
		// 	selectItem.value = JSON.parse(infoData);

		// }
		console.log("onReady====", selectItem.value.orderNumber);
	});
</script>

<style scoped lang="scss">
	view {
		box-sizing: border-box;
		color: $uni-text-color;
	}

	button {
		font-size: 13px !important;
	}

	.uniInput {
		border-bottom: 1px solid $uni-border-bar-color;
		border-radius: 0px;
		width: 60px;
		padding: 3px 3px;
		font-size: 13px;
	}

	.searchForm {
		background-color: $uni-bg-color;
		padding: 15rpx 0rpx;
	}

	.searchForm>view:nth-child(1) {
		padding: 0 7px;
	}

	.searchFormList {
		font-weight: bold !important;
	}

	.btnList2 {
		display: flex;
		flex-flow: row wrap;
		justify-content: space-between;
		align-content: space-between;
	}

	.btnList2>view {
		flex: 1;
		padding: 8rpx;

	}

	.btnList {
		display: flex;
		flex-flow: row wrap;
		justify-content: space-between;
		align-content: space-between;
	}

	.btnList>view {
		flex: 33%;
		padding: 8rpx;
	}

	.example-body {
		margin-top: 10rpx;
	}

	.itemCard {
		padding-left: 15rpx;
		z-index: 1;
		border: 1px solid $uni-border-color;
		border-radius: 6px;
		background-color: white;
		margin: 8rpx 8rpx 0px 8rpx;
	}

	.listItme {
		display: flex;
		justify-content: row;
		line-height: 55rpx;
	}

	.listTitle {
		font-weight: bold;
		padding-left: 20rpx;
		min-width: 120rpx;
		padding-right: 10rpx;
		white-space: nowrap;
		/* 禁止换行 */
		overflow: hidden;

	}

	.listTitle::after {
		white-space: pre;
		content: "";
	}

	.listContent2 {
		display: flex;
		padding-left: 12px;
	}

	.listContent2>button {
		width: 140px !important;
		font-size: 11px !important;
	}

	.listContent {
		min-width: 120rpx;
		margin-right: 10rpx;
		white-space: nowrap;
		/* 禁止换行 */
		overflow: hidden;
		/* 隐藏溢出内容 */
		text-overflow: ellipsis;
		/* 使用省略号表示溢出 */
	}

	.item_line {
		height: 1rpx;
		width: 100%;
		background-color: #eeeeee;
		margin: 10rpx 0;
	}

	.collapseBody {
		padding: 0 40rpx;
	}

	.popup-content {

		margin: 0rpx 40rpx 40rpx 40rpx;
		background-color: #fff;

	}

	//蓝色条块
	.blueBar {
		margin-left: -22rpx;
	}

	.blueBar::before {
		white-space: pre;
		content: " ";
		background-color: $uni-border-bar-color; // $uni-color-primary ;
		width: 4px;
		/* 竖块的宽度 */
		height: 12px;
		border-radius: 10px;
		margin-right: 15rpx;
	}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/956113.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

移动端布局 ---- 学习分享

响应式布局实现方法 主流的实现方案有两种: 通过rem \ vw/vh \ 等单位,实现在不同设备上显示相同比例进而实现适配. 响应式布局,通过媒体查询media 实现一套HTML配合多套CSS实现适配. 在学习移动端适配之前,还需要学习移动端适配原理: 移动端适配原理(Viewport) 了解VSCo…

STM32 FreeROTS 任务创建和删除实验(静态方法)

实验目标 学会 xTaskCreateStatic( )和 vTaskDelete( ) 的使用&#xff1a; start_task&#xff1a;用来创建其他的三个任务。task1&#xff1a;实现LED1每500ms闪烁一次。task2&#xff1a;实现LED2每500ms闪烁一次。 task3&#xff1a;判断按键KEY1是否按下&#xff0c;按…

家政服务小程序,打造智慧家政新体验

春节即将来临&#xff0c;家政市场呈现出了火热的场景&#xff0c;大众对家政服务的需求持续增加。 近年来&#xff0c;家政市场开始倾向数字化、智能化&#xff0c;借助科学技术打造家政数字化平台&#xff0c;让大众在手机上就可以预约家政服务&#xff0c;减少传统家政市场…

《贪心算法:原理剖析与典型例题精解》

必刷的贪心算法典型例题&#xff01; 算法竞赛&#xff08;蓝桥杯&#xff09;贪心算法1——数塔问题-CSDN博客 算法竞赛&#xff08;蓝桥杯&#xff09;贪心算法2——需要安排几位师傅加工零件-CSDN博客 算法&#xff08;蓝桥杯&#xff09;贪心算法3——二维数组排序与贪心算…

Vue3 nginx 打包后遇到的问题

前端vite文件配置 export default defineConfig({plugins: [vue(),DefineOptions()],base:./,resolve:{alias:{:/src, //配置指向src目录components:/src/components,views:/src/views}},server:{// host:0.0.0.0,// port:7000,proxy:{/api:{target:xxx, // 目标服务器地址 &am…

linux下配置python环境及库配置

概述 使用Linux作为开发环境拥有完整的开源工具链且易于通过系统包管理器安装&#xff0c;与系统集成良好。对于开源项目开发&#xff0c;能方便地从源代码编译安装软件&#xff0c;使用构建工具&#xff0c;提供更原生的开发环境。 可高度定制系统&#xff0c;满足特殊开发需求…

【机器学习实战入门】使用OpenCV进行性别和年龄检测

Gender and Age Detection Python 项目 首先,向您介绍用于此高级 Python 项目的性别和年龄检测中的术语: 什么是计算机视觉? 计算机视觉是一门让计算机能够像人类一样观察和识别数字图像和视频的学科。它面临的挑战大多源于对生物视觉有限的了解。计算机视觉涉及获取、处…

左神算法基础提升--4

文章目录 树形dp问题Morris遍历 树形dp问题 求解这个问题需要用到我们在基础班上学到的从节点的左子树和右子树上拿信息的方法。 求最大距离主要分为两种情况&#xff1a;1.当前节点参与最大距离的求解&#xff1b;2.当前节点不参与最大距离的求解&#xff1b; 1.当前节点参与最…

spark任务优化参数整理

以下参数中有sql字眼的一般只有spark-sql模块生效&#xff0c;如果你看过spark的源码&#xff0c;你会发现sql模块是在core模块上硬生生干了一层&#xff0c;所以反过来spark-sql可以复用core模块的配置&#xff0c;例外的时候会另行说明&#xff0c;此外由于总结这些参数是在不…

华为数据中心CE系列交换机级联M-LAG配置示例

M-LAG组网简介 M-LAG&#xff08;Multi-chassis Link Aggregation&#xff09;技术是一种跨设备的链路聚合技术&#xff0c;它通过将两台交换机组成一个逻辑设备&#xff0c;实现链路的负载分担和故障切换&#xff0c;从而提高网络的可靠性和稳定性。下面给大家详细介绍如何在…

游戏引擎学习第80天

Blackboard&#xff1a;增强碰撞循环&#xff0c;循环遍历两种类型的 t 值 计划对现有的碰撞检测循环进行修改&#xff0c;以便实现一些新的功能。具体来说&#xff0c;是希望处理在游戏中定义可行走区域和地面的一些实体。尽管这是一个2D游戏&#xff0c;目标是构建一些更丰富…

EMS专题 | 守护数据安全:数据中心和服务器机房环境温湿度监测

您需要服务器机房温度监测解决方案吗&#xff1f; 服务器机房是企业中用于存储、管理和维护服务器及其相关组件的设施。服务器机房通常位于数据中心内&#xff0c;是一个专门设计的物理环境&#xff0c;旨在确保服务器的稳定运行和数据的安全性。服务器机房主要起到存储和管理数…

4 AXI USER IP

前言 使用AXI Interface封装IP&#xff0c;并使用AXI Interface实现对IP内部寄存器进行读写实现控制LED的demo&#xff0c;这个demo是非常必要的&#xff0c;因为在前面的笔记中基本都需哟PS端与PL端就行通信互相交互&#xff0c;在PL端可以通过中断的形式来告知PS端一些事情&…

网络编程 | UDP套接字通信及编程实现经验教程

1、UDP基础 传输层主要应用的协议模型有两种&#xff0c;一种是TCP协议&#xff0c;另外一种则是UDP协议。在上一篇博客文章中&#xff0c;已经对TCP协议及如何编程实现进行了详细的梳理讲解&#xff0c;在本文中&#xff0c;主要讲解与TCP一样广泛使用了另一种协议&#xff1a…

A5.Springboot-LLama3.2服务自动化构建(二)——Jenkins流水线构建配置初始化设置

下面我们接着上一篇文章《A4.Springboot-LLama3.2服务自动化构建(一)——构建docker镜像配置》继续往下分析,在自动化流水线构建过程当中的相关初始化设置和脚本编写。 一、首先需要先安装Jenkins 主部分请参考我前面写的一篇文章《Jenkins持续集成与交付安装配置》 二、…

开发神器之cursor

文章目录 cursor简介主要特点 下载cursor页面的简单介绍切换大模型指定ai学习的文件指定特定的代码喂给ai创建项目框架文件 cursor简介 Cursor 是一款专为开发者设计的智能代码编辑器&#xff0c;集成了先进的 AI 技术&#xff0c;旨在提升编程效率。以下是其主要特点和功能&a…

基于机器学习随机森林算法的个人职业预测研究

1.背景调研 随着信息技术的飞速发展&#xff0c;特别是大数据和云计算技术的广泛应用&#xff0c;各行各业都积累了大量的数据。这些数据中蕴含着丰富的信息和模式&#xff0c;为利用机器学习进行职业预测提供了可能。机器学习算法的不断进步&#xff0c;如深度学习、强化学习等…

【王树森搜索引擎技术】概要01:搜索引擎的基本概念

1. 基本名词 query&#xff1a;查询词SUG&#xff1a;搜索建议文档&#xff1a;搜索结果标签/筛选项 文档单列曝光 文档双列曝光 2. 曝光与点击 曝光&#xff1a;用户在搜索结果页上看到文档&#xff0c;就算曝光文档点击&#xff1a;在曝光后&#xff0c;用户点击文档&…

图论DFS:黑红树

我的个人主页 {\large \mathsf{{\color{Red} 我的个人主页} } } 我的个人主页 往 {\color{Red} {\Huge 往} } 往 期 {\color{Green} {\Huge 期} } 期 文 {\color{Blue} {\Huge 文} } 文 章 {\color{Orange} {\Huge 章}} 章 DFS 算法&#xff1a;记忆化搜索DFS 算法&#xf…

ros2-7.5 做一个自动巡检机器人

7.5.1 需求及设计 又到了小鱼老师带着做最佳实践项目了。需求&#xff1a;做一个在各个房间不断巡逻并记录图像的机器人。 到达目标点后首先通过语音播放到达目标点信息&#xff0c; 再通过摄像头拍摄一张图片保存到本地。 7.5.2 编写巡检控制节点 在chapt7_ws/src下新建功…