Browse Source

用户下单---志愿者注册优化

jiayubo 6 days ago
parent
commit
69dcbe6f3e

+ 10 - 10
components/Client/new_file.vue

@@ -71,14 +71,14 @@
 			name: '孩子成长',
 			key: 2,
 		},
-		{
-			icon: '/static/Tioimages/家庭保洁.png',
-			name: '家庭保洁',
-			key: 3,
-		},
 		{
 			icon: '/static/Tioimages/临时帮手.png',
 			name: '临时帮手',
+			key: 3,
+    },
+    	{
+			icon: '/static/Tioimages/家庭保洁.png',
+			name: '家庭保洁',
 			key: 4,
 		},
 		{
@@ -134,14 +134,14 @@
 			name: '孩子成长',
 			key: 2,
 		},
-		{
-			icon: '/static/Tioimages/家庭保洁.png',
-			name: '家庭保洁',
-			key: 3,
-		},
 		{
 			icon: '/static/Tioimages/临时帮手.png',
 			name: '临时帮手',
+			key: 3,
+    },
+    {
+			icon: '/static/Tioimages/家庭保洁.png',
+			name: '家庭保洁',
 			key: 4,
 		},
 		{

+ 130 - 10
components/its-calendar/its-calendar.vue

@@ -43,10 +43,14 @@
 				type: Array,
 				default: () => []
 			},
-			businessDuration: {
+			businessDuration: {  // 从父组件接收的服务时长(分钟)
 				type: Number,
 				default: 0
-			} // 从父组件接收的服务时长(分钟)
+			},
+			minQuantity: {  // 从父组件接收的购买次数(次数)
+				type: Number,
+				default: 1
+			}
 		},
 		data() {
 			return {
@@ -58,6 +62,8 @@
 				selectedTime: null,
 				selectedDay: null, // 新增:记录选择的日期
 				selectedTimeSlots: {}, // 存储每个日期的选择状态
+				upItem: null, //  保留上一个点击的item,外部情况下再刷新状态
+				filteredSlots: []
 			}
 		},
 		watch: {
@@ -93,33 +99,43 @@
 		methods: {
 			// 恢复选择状态
 			restoreSelections() {
+				console.log('>>>>>>执行了', this.selectedTimeSlots);
 				if (!this.timeHostArr[this.day_index]) return;
 				
+				console.log('>>>>>>执行了2', this.selectedTimeSlots);
+				
 				// 遍历当前日期的时间槽
 				this.timeHostArr[this.day_index].forEach(slot => {
 					// 检查这个时间槽是否在之前被选中
 					const dateKey = this.timeArr[this.day_index];
+					console.log(dateKey, '>>>>dateKey');
+					console.log(this.selectedTimeSlots[dateKey], '>>>>this.selectedTimeSlots[dateKey]');
 					if (this.selectedTimeSlots[dateKey] && 
 						this.selectedTimeSlots[dateKey].includes(slot.timeStamp)) {
 						slot.checked = true;
 						
 						// 计算并禁用后续的时间段
-						const seconds = this.businessDuration * 60;
+						const seconds = this.businessDuration * this.minQuantity * 60;
+						// const startTimestamp = 
 						const endTimestamp = slot.timeStamp + seconds;
 						
 						// 禁用后续的时间段
 						this.timeHostArr[this.day_index].forEach(s => {
-							if (s.timeStamp > slot.timeStamp && 
-								s.timeStamp <= endTimestamp) {
-								s.disabled = true;
+							// 差一个范围控制变量,是否在可选择范围内
+							if (s.timeStamp > slot.timeStamp && s.timeStamp <= endTimestamp) {
+								s.disabled = true
 							}
 						});
+						
+						
 					}
 				});
 			},
 			
 			handleTimeClick(item) {
-				const durationMs = this.businessDuration * 60 * 1000;
+				this.upItem = item
+				
+				const durationMs = this.businessDuration  * this.minQuantity * 60 * 1000;
 				const itemTime = item.timeStamp > 9999999999 ? item.timeStamp : item.timeStamp * 1000;
 				const slots = this.timeHostArr[this.day_index] || [];
 				const currentDate = this.timeArr[this.day_index];
@@ -160,12 +176,25 @@
 				}
 
 				// 3. 其他逻辑保持不变
-				const seconds = this.businessDuration * 60;
+				const seconds = this.businessDuration  * this.minQuantity * 60;
 				const endTimestamp = (item.timeStamp + seconds);
 				const filteredSlots = this.timeHostArr[this.day_index].filter(i => {
 				   return (i.timeStamp > item.timeStamp && 
 				          i.timeStamp <= endTimestamp)
-				 });
+				});
+				this.filteredSlots = filteredSlots
+				 
+				 console.log(filteredSlots, '》》》》》》filteredSlots');
+				 
+				// 时间差值(数组中的最后一项 - 当前点击项)
+				const timestampDifferenceValue = filteredSlots.length ? (filteredSlots[filteredSlots.length - 1].timeStamp - item.timeStamp) * 1000 : 0
+				
+				// 选择时间,后续服务时间是否充足,不充足结束逻辑执行 timeStamp
+				if (timestampDifferenceValue < durationMs) { // 所选时间差值 小于 服务时间值 结束执行
+					uni.showToast({ title: '所选时间的服务时间不充足!', icon: 'none' });
+					return false
+				}
+				 
 				if (!item.checked) {
 					filteredSlots.forEach(v => {
 						v.disabled = true
@@ -189,6 +218,97 @@
 				}
 				this.hosts(item);
 			},
+			
+			async handleTimeClick2() {
+				
+				let item = this.upItem
+				
+				// console.log(this.timeHostArr[this.day_index], '>>>this.timeHostArr[this.day_index]');
+				
+				// 处理减法时 (选中状态重置)
+				this.timeHostArr[this.day_index].forEach(s => {
+					 s.disabled = item.hasReservation === 1
+				});
+				// this.hosts(item);
+				
+				// console.log(this.timeArr, '.>>>>item1111');
+				
+				const durationMs = this.businessDuration  * this.minQuantity * 60 * 1000;
+				const itemTime = item.timeStamp > 9999999999 ? item.timeStamp : item.timeStamp * 1000;
+				const slots = this.timeHostArr[this.day_index] || [];
+				const currentDate = this.timeArr[this.day_index];
+				
+				// 1. 判断当前时间是否在任何已预约时间段的服务时长范围内
+				let inReservedRange = false;
+				for (let slot of slots) {
+					if (slot.hasReservation === 1) {
+						const reservedTime = slot.timeStamp > 9999999999 ? slot.timeStamp : slot.timeStamp * 1000;
+						const start = reservedTime - durationMs;
+						const end = reservedTime + durationMs;
+						if (itemTime >= start && itemTime < end) {
+							inReservedRange = true;
+							break;
+						}
+					}
+				}
+				if (inReservedRange) {
+					uni.showToast({ title: '服务时长不足,请选择其他时间段', icon: 'none' });
+					return false;
+				}
+				
+				// 2. 判断当前点击时间与所有已选时间段的服务时长有无重叠(多选判断)
+				const selectedArr = this.selectedTimeSlots[currentDate] || [];
+				if (!item.checked) { // 只在选中时判断
+					for (let t of selectedArr) {
+						if (t === item.timeStamp) continue; // 跳过自己
+						const tStart = t > 9999999999 ? t : t * 1000;
+						const tEnd = tStart + durationMs;
+						if (
+							(itemTime >= tStart && itemTime < tEnd) ||
+							(itemTime < tStart && itemTime + durationMs > tStart)
+						) {
+							uni.showToast({ title: '时间段不在服务范围内', icon: 'none' });
+							return false;
+						}
+					}
+				}
+				
+				// 3. 其他逻辑保持不变
+				const seconds = this.businessDuration  * this.minQuantity * 60;
+				// console.log(this.businessDuration, this.minQuantity, '>>>>>9999');
+				const endTimestamp = (item.timeStamp + seconds);
+				// console.log(endTimestamp, '>>>>>>endTimestamp');
+				const filteredSlots = this.timeHostArr[this.day_index].filter(i => {
+				   return (i.timeStamp > item.timeStamp && 
+				          i.timeStamp <= endTimestamp)
+				 });
+				 this.filteredSlots = filteredSlots
+				 
+				 // console.log(filteredSlots, '》》》》》》filteredSlots');
+				 
+				// 时间差值(数组中的最后一项 - 当前点击项)
+				const timestampDifferenceValue = filteredSlots.length ? (filteredSlots[filteredSlots.length - 1].timeStamp - item.timeStamp) * 1000 : 0
+				
+				// 选择时间,后续服务时间是否充足,不充足结束逻辑执行 timeStamp
+				if (timestampDifferenceValue < durationMs) { // 所选时间差值 小于 服务时间值 结束执行
+					uni.showToast({ title: '所选时间的服务时间不充足!', icon: 'none' });
+					return false
+				}
+				
+				filteredSlots.forEach(v => {
+					v.disabled = true
+				});
+				
+				console.log('>>>>>执行');
+				
+				if (!this.selectedTimeSlots[currentDate]) {
+					this.selectedTimeSlots[currentDate] = [];
+				}
+				this.selectedTimeSlots[currentDate].push(item.timeStamp);
+				
+				// this.hosts(item);
+			},
+			
 			// 点击日期
 			dayList(e, index) {
 				this.day_index = index
@@ -217,7 +337,7 @@
 
 				this.host_index = item.timeStamp; // 显示用原始值
 				this.selectedTime = itemTime;
-				this.disableAfterTime = itemTime + (this.businessDuration * 60 * 1000);
+				this.disableAfterTime = itemTime + (this.businessDuration * this.minQuantity * 60 * 1000);
 				this.$emit('getByTime', {
 					...item,
 					timeStamp: itemTime // 传递转换后的值

+ 2 - 2
config.js

@@ -2,8 +2,8 @@
 const config = {
 	// baseUrl: 'https://vue.ruoyi.vip/prod-api',
 	//cloud后台网关地址
-	baseUrl: 'http://192.168.100.95:9527',//嵘
-	// baseUrl: 'http://192.168.100.121:9527',//龙
+	baseUrl: 'http://192.168.100.95:9527',//嵘
+	// baseUrl: 'http://192.168.100.121:9527',//龙
 	// baseUrl: 'https://zybooks.tech/prod-api',
 	mapKey:'KFEBZ-P2GKZ-A5PX4-7Q6Y7-KXOBF-XCB4C',
 	appName: '金邻助家',

+ 75 - 12
pages_home/pages/Volunteerside/goodsDetails.vue

@@ -26,6 +26,9 @@
               <view class="Telephone"
                 >服务时长:{{ listData.businessDuration }}</view
               >
+              <view class="Telephone"
+                >次/件:{{ listData.minQuantity + listData.businessUnit }}</view
+              >
               <view class="Telephone">电话:{{ listData.phonenumber }}</view>
             </view>
 
@@ -34,6 +37,17 @@
           </view>
         </template>
       </up-card>
+
+      <up-card
+        title="服务描述"
+        :thumb="thumb"
+        :head-style="{ height: '80rpx', padding: '20rpx' }"
+      >
+        <template #body>
+          {{ listData.businessDescribe }}
+        </template>
+      </up-card>
+
       <up-card
         title="技能介绍"
         :thumb="thumb"
@@ -88,13 +102,21 @@
         <up-divider></up-divider>
         <view class="Wrap-content1">
           <up-avatar :src="src"></up-avatar>
-          <text style="margin-top: 15rpx">¥{{ listData.businessPrice }}</text>
+          <text style="margin-top: 15rpx">¥{{ businessPrice }}</text>
         </view>
         <view class="Wrap-info">
           <text style="margin-left: 10rpx">服务类别:</text>
           <up-text type="info" text="家庭教育"></up-text>
         </view>
+        <view class="Wrap-info">
+           <text style="margin-left: 10rpx">服务次数:</text>
+           <up-number-box :modelValue="singleQuantity" :min="listData.minQuantity" @change="valChange"></up-number-box>
+        </view>
 
+        <view class="Wrap-info">
+           <text style="margin-left: 10rpx">购买规格:</text>
+            <view>{{ listData.projectTypeName + listData.minQuantity }}</view>
+          </view>
         <view class="Wrap-content3">
           <text style="margin-left: 15rpx">服务周期:</text>
           <view style="margin-top: 25rpx">
@@ -108,7 +130,9 @@
           <view>
             <its-calendar
               v-if="show"
+			         ref="itsCalendarRef"
               :businessDuration="listData.businessDuration"
+              :minQuantity="singleQuantity"
               :timeArr="doorToDoorTimeArr"
               :timeHostArr="timeHostArr"
               @getByDate="getByDate"
@@ -293,7 +317,7 @@
 
 <script setup>
 import { onLoad } from '@dcloudio/uni-app'
-import { onMounted, ref, reactive, computed } from 'vue'
+import { onMounted, ref, reactive, computed, nextTick } from 'vue'
 import {
   getDetailsvolunteerId,
   volunteerwork,
@@ -309,8 +333,9 @@ import addressComponent from '@/pages_home/components/volunteerData/adresss.vue'
 const src = ref(
   'http://pic2.sc.chinaz.com/Files/pic/pic9/202002/hpic2119_s.jpg'
 )
+const itsCalendarRef = ref(null)
 const businessPrice = ref() //价格
-const addressId = ref('') //用户下单地址
+const singleQuantity = ref(1) // 购买次数,默认为1,不可为0
 const volunteerId = ref('') // 存储志愿者ID
 const serviceCategory = ref('') //存储大类别
 const businessManagementId = ref('') //具体分类id
@@ -323,19 +348,17 @@ const selectedTimes = ref([]) // 存储所有选择的时间对象
 const totalTimes = ref(0) // 时间点 点击的次数
 const startDisabled = ref('')
 const endDisabled = ref('')
-const time = ref('')
 const listData = ref({})
 const show = ref(false) //第一层弹框
 const showSecond = ref(false) //第二层弹框
-const showThird = ref(false) //第二层弹框
-const showSum = ref(false)
 const remark = ref('') //备注
-const radiovalue1 = ref('苹果')
+const radiovalue1 = ref(null)
 const selectedAddress = ref(null)
 const addressFlag = ref(false)
 const paymentMethod = ref(null) // Add payment method ref with default value 1 (wallet)
 
-const addressInfo = ref(null)
+const addressInfo = ref(null)  
+const value = ref(1);  // 将在onLoad中更新为listData.minQuantity
 // Radio 单选框数据
 const radiolist1 = reactive([
   {
@@ -451,9 +474,47 @@ onLoad(async (options) => {
   })
 
   listData.value = res.data || {}
+  
+  // 更新步进器的初始值为listData中的minQuantity
+  value.value = listData.value.minQuantity || 1
   businessPrice.value = listData.value.businessPrice || 0
+  singleQuantity.value = listData.value.minQuantity || 1
 })
 
+// 添加步进器值变化处理函数
+const valChange = (obj) => {
+	const durationMs = listData.value.businessDuration  * obj.value * 60;
+	
+	const a = itsCalendarRef.value.upItem
+	const b = itsCalendarRef.value.timeHostArr
+	const c = itsCalendarRef.value.day_index
+	
+	const seconds = listData.value.businessDuration  * obj.value * 60;
+	const endTimestamp = (a.timeStamp + durationMs);
+	
+	// 根据当前时间分割出后续应选数据数组
+	const filteredSlots = b[c].filter(i => {
+	   return (i.timeStamp > a.timeStamp && 
+	          i.timeStamp <= endTimestamp)
+	});
+	
+	const timestampDifferenceValue = filteredSlots.length ? (filteredSlots[filteredSlots.length - 1].timeStamp - a.timeStamp) : 0
+	
+	console.log(filteredSlots, '>>>>>filteredSlots');
+	
+	
+	if (timestampDifferenceValue < durationMs) { // 所选时间差值 小于 服务时间值 结束执行
+		uni.showToast({ title: '所选时间的服务时间不充足!', icon: 'none' });		
+		return false
+	}
+	
+	singleQuantity.value = obj.value || 1;
+	
+	nextTick(() => {
+		itsCalendarRef.value.handleTimeClick2()
+	})
+}
+
 // 选择日期
 const getListTime = async () => {
   const params = {
@@ -565,7 +626,8 @@ const upPopupOpen = () => {
 }
 
 const computeMoney = computed(() => {
-  return totalTimes.value * businessPrice.value
+	console.log(totalTimes.value , businessPrice.value , singleQuantity.value, '>>>>>>	console.log(totalTimes.value * businessPrice.value * minQuantity.value);');
+  return totalTimes.value * businessPrice.value * singleQuantity.value
 })
 
 const certificationPictures = computed(() => {
@@ -611,6 +673,7 @@ const proceedToPayment = async () => {
         businessManagementId: businessManagementId.value,
         volunteerInfoId: listData.value.volunteerInfoId,
         addressId: selectedAddress.value.addressId,
+        singleQuantity: singleQuantity.value,
       },
       workDateList: [],
     }
@@ -688,7 +751,7 @@ const proceedToPayment = async () => {
               })
               reject(error)
             }
-          }
+          },
         })
       })
     }
@@ -702,7 +765,7 @@ const proceedToPayment = async () => {
       })
       setTimeout(() => {
         uni.reLaunch({
-          url: '/pages/classify'
+          url: '/pages/classify',
         })
       }, 1500)
     }
@@ -786,7 +849,7 @@ onMounted(async () => {
 /* 左侧图片 */
 .card-image {
   width: 240rpx;
-  height: 340rpx;
+  height: 380rpx;
   border-radius: 12rpx;
   margin-right: 30rpx;
   flex-shrink: 0;

+ 185 - 125
pages_home/pages/register/data.js

@@ -1,151 +1,211 @@
 import { getTreeList } from '@/api/volunteer'
 
 const sex_option = [
+  {
+    name: '男',
+  },
+  {
+    name: '女',
+  },
+]
+
+const city_option = [
+  {
+    name: '重庆',
+  },
+  {
+    name: '四川',
+  },
+]
+
+const business_unit_options = [
+  {
+    name: '件数',
+    value: 1,
+  },
+  {
+    name: '面积',
+    value: 2,
+  },
+   {
+    name: '时间',
+    value: 3,
+  },
+]
+
+const rules = {
+  name: [
     {
-        name: '男',
+      type: 'string',
+      required: true,
+      message: '请填写姓名',
+      trigger: ['blur', 'change'],
     },
     {
-        name: '女',
+      // 此为同步验证,可以直接返回true或者false,如果是异步验证,稍微不同,见下方说明
+      validator: (rule, value, callback) => {
+        // 调用uview-plus自带的js验证规则,详见:https://uview-plus.jiangruyi.com/js/test.html
+        return uni.$u.test.chinese(value)
+      },
+      message: '姓名必须为中文',
+      // 触发器可以同时用blur和change,二者之间用英文逗号隔开
+      trigger: ['change', 'blur'],
     },
-]
-
-const city_option = [
+  ],
+  sex: [
     {
-        name: '重庆',
+      type: 'string',
+      max: 1,
+      required: true,
+      message: '请选择男或女',
+      trigger: ['blur', 'change'],
     },
+  ],
+  age: [
     {
-        name: '四川',
+      type: 'string',
+      required: true,
+      message: '请填写年龄',
+      trigger: ['blur', 'change'],
     },
-]
-
-const rules = {
-    name: [
-        {
-            type: 'string',
-            required: true,
-            message: '请填写姓名',
-            trigger: ['blur', 'change']
-        },
-        {
-            // 此为同步验证,可以直接返回true或者false,如果是异步验证,稍微不同,见下方说明
-            validator: (rule, value, callback) => {
-                // 调用uview-plus自带的js验证规则,详见:https://uview-plus.jiangruyi.com/js/test.html
-                return uni.$u.test.chinese(value);
-            },
-            message: "姓名必须为中文",
-            // 触发器可以同时用blur和change,二者之间用英文逗号隔开
-            trigger: ["change", "blur"],
-        },
-    ],
-    sex: [
-        {
-            type: 'string',
-            max: 1,
-            required: true,
-            message: '请选择男或女',
-            trigger: ['blur', 'change']
-        }
-    ],
-    age: [
-        {
-            type: 'string',
-            required: true,
-            message: '请填写年龄',
-            trigger: ['blur', 'change']
-        }
-    ],
-    idCard: [
-        {
-            type: 'string',
-            required: true,
-            message: '请填写证件号',
-            trigger: ['blur', 'change']
-        }
-    ],
-    districtName: [
-        {
-            type: 'string',
-            required: true,
-            message: '请选择地区',
-            trigger: ['blur', 'change']
-        }
-    ],
-    address: [
-        {
-            type: 'string',
-            required: true,
-            message: '请填写地址',
-            trigger: ['blur', 'change']
-        }
-    ],
-    skillDescribe:[
-        {
-            type: 'string',
-            required: true,
-            message: '请填写技能简介',
-            trigger: ['blur', 'change']
-        }
-    ],
-    businessManagementId: [
-        {
-            type: 'string',
-            required: true,
-            message: '请选择服务项目',
-            trigger: ['blur', 'change']
-        }
-    ]
-}
-
-
-const column = [
+  ],
+  idCard: [
     {
-        label: "姓名",
-        key: "name",
-        type: "input",
-        rules: rules.name,
-        required:true
+      type: 'string',
+      required: true,
+      message: '请填写证件号',
+      trigger: ['blur', 'change'],
     },
+  ],
+  districtName: [
     {
-        label: "性别",
-        key: "sex",
-        type: "select",
-        option: sex_option,
-        rules: rules.sex,
-        required:true
+      type: 'string',
+      required: true,
+      message: '请选择地区',
+      trigger: ['blur', 'change'],
     },
+  ],
+  address: [
     {
-        label: "年龄",
-        key: "age",
-        type: "input",
-        rules: rules.age,
-        required:true
+      type: 'string',
+      required: true,
+      message: '请填写地址',
+      trigger: ['blur', 'change'],
     },
+  ],
+  minQuantity: [
     {
-        label: "手机号",
-        key: "phonenumber",
-        type: "phone-code",
+      type: 'string',
+      required: true,
+      message: '请填写最少购买',
+      trigger: ['blur', 'change'],
     },
+  ],
+  skillDescribe: [
     {
-        label: "地区",
-        key: "districtName",
-        type: "city",
-        option: city_option,
-        rules: rules.districtName,
-        required:true
+      type: 'string',
+      required: true,
+      message: '请填写技能简介',
+      trigger: ['blur', 'change'],
     },
+  ],
+  businessManagementId: [
     {
-        label: "详细地址",
-        key: "address",
-        type: "input",
-        rules: rules.address,
-        required:true
+      type: 'string',
+      required: true,
+      message: '请选择服务项目',
+      trigger: ['blur', 'change'],
     },
+  ],
+  businessDescribe: [
+    {
+      type: 'string',
+      required: true,
+      message: '请填写服务描述',
+      trigger: ['blur', 'change']
+    }
+  ],
+  businessUnit: [
     {
-        label: "技能简介",
-        key: "skillDescribe",
-        type: "textarea",
-        rules: rules.skillDescribe,
-        required:true
+      type: 'string',
+      required: true,
+      message: '请选择购买单位',
+      trigger: ['blur', 'change'],
     },
+  ],
+}
+
+const column = [
+  {
+    label: '姓名',
+    key: 'name',
+    type: 'input',
+    rules: rules.name,
+    required: true,
+  },
+  {
+    label: '性别',
+    key: 'sex',
+    type: 'select',
+    option: sex_option,
+    rules: rules.sex,
+    required: true,
+  },
+  {
+    label: '年龄',
+    key: 'age',
+    type: 'input',
+    rules: rules.age,
+    required: true,
+  },
+  {
+    label: '手机号',
+    key: 'phonenumber',
+    type: 'phone-code',
+  },
+  {
+    label: '地区',
+    key: 'districtName',
+    type: 'city',
+    option: city_option,
+    rules: rules.districtName,
+    required: true,
+  },
+  {
+    label: '详细地址',
+    key: 'address',
+    type: 'input',
+    rules: rules.address,
+    required: true,
+  },
+  {
+    label: '最少购买',
+    key: 'minQuantity',
+    type: 'input',
+    rules: rules.minQuantity,
+    required: true,
+  },
+  {
+    label: '购买单位',
+    key: 'businessUnit',
+    type: 'select',
+    option: business_unit_options,
+    rules: rules.businessUnit,
+    required: true,
+  },
+  {
+    label: '技能简介',
+    key: 'skillDescribe',
+    type: 'textarea',
+    rules: rules.skillDescribe,
+    required: true,
+  },
+  {
+    label: '服务描述',
+    key: 'businessDescribe',
+    type: 'textarea',
+    rules: rules.businessDescribe,
+    required: true,
+  },
 ]
-export {  column}
+export { column }