diff --git a/pages/freight-calculator/index.js b/pages/freight-calculator/index.js index ec34068..3915ae4 100644 --- a/pages/freight-calculator/index.js +++ b/pages/freight-calculator/index.js @@ -24,14 +24,6 @@ Page({ quantity: 1, // 数量 type: 'egg' // 货物类型,默认为鸡蛋 }, - // 物流车辆信息 - vehicleInfo: { - type: '', // 车辆类型 - capacity: '', // 载重能力(吨) - number: '', // 车辆编号 - driver: '', // 司机姓名 - phone: '' // 联系电话 - }, // 物流人员信息 logisticsPersonnel: [ { @@ -56,10 +48,6 @@ Page({ phone: '13700137000' } ], - // 车辆类型列表 - vehicleTypes: ['厢式货车', '冷藏车', '平板车', '高栏车', '自卸车'], - // 选中的车辆类型索引 - selectedVehicleTypeIndex: 0, // 选中的货源信息 selectedGoods: {}, // 选中的规格索引 @@ -83,6 +71,84 @@ Page({ pickerValues: { origin: [0, 0, 0], destination: [0, 0, 0] + }, + // 运输模式 + transportModes: ['货拉拉', '运满满', '零担拼车'], + transportModeIndex: 0, + + // 包装类型 + packagingTypes: ['标准包装', '加强包装'], + packagingIndex: 0, + + // 车型 + vehicleTypes: ['面包车', '依维柯', '平板', '厢式', '高栏车', '集装箱', '自卸', '冷藏', '保温', '棉被车', '爬梯车', '飞翼车', '高低板'], + vehicleTypeIndex: 0, + + // 车长 + truckLengths: ['1.4米', '1.8米', '2.7米', '3.3米', '3.8米', '4.2米'], + truckLengthIndex: 5, + + // 天气状况 + weatherTypes: ['晴朗', '多云', '小雨', '中雨', '大雨', '暴雨', '雪', '雾'], + weatherIndex: 0, + + // 是否节假日 + holidayOptions: ['否', '是'], + holidayIndex: 0, + + // 时间段 + timePeriods: ['上午', '下午', '晚上'], + timePeriodIndex: 0, + + // 输入值 + distance: '', + weight: '', + volume: '', + waitTime: '40', + + // 车型信息 + vehicleInfo: '面包车(1.4米): 0-500kg, 0-5m³', + + // 计算结果 + showResult: false, + feeRange: '', + marketPrice: '', + complianceStatus: '', + isCompliant: true, + detailText: '', + + // 车型和车长对应关系 + vehicleLengthMap: { + '面包车': ['1.4米', '1.8米', '2.7米', '3.3米', '3.8米', '4.2米'], + '依维柯': ['3.3米', '3.8米', '4.2米', '5米'], + '平板': ['4.2米', '5米', '6.2米', '6.8米', '7.7米', '8.2米', '8.7米', '9.6米', '11.7米', '12.5米', '13米', '13.7米', '15米', '16米', '17.5米'], + '厢式': ['4.2米', '5米', '6.2米', '6.8米', '7.7米', '8.2米', '8.7米', '9.6米', '11.7米', '12.5米'], + '高栏车': ['4.2米', '5米', '6.2米', '6.8米', '7.7米', '8.2米', '8.7米', '9.6米', '11.7米', '12.5米', '13米', '13.7米', '15米', '16米', '17.5米'], + '集装箱': ['9.6米', '11.7米', '12.5米', '13米', '13.7米', '15米', '16米', '17.5米'], + '自卸': ['4.2米', '5米', '6.2米', '6.8米', '7.7米', '8.2米', '8.7米', '9.6米'], + '冷藏': ['4.2米', '5米', '6.2米', '6.8米', '7.7米', '8.2米', '8.7米', '9.6米'], + '保温': ['4.2米', '5米', '6.2米', '6.8米'], + '棉被车': ['4.2米', '5米', '6.2米', '6.8米', '7.7米'], + '爬梯车': ['6.8米', '7.7米', '8.2米', '8.7米', '9.6米'], + '飞翼车': ['4.2米', '5米', '6.2米', '6.8米', '7.7米', '8.2米', '8.7米', '9.6米'], + '高低板': ['6.8米', '7.7米', '8.2米', '8.7米', '9.6米', '11.7米', '12.5米', '13米', '13.7米', '15米', '16米', '17.5米'] + }, + + // 基础车型信息 + baseInfo: { + '面包车': ['0-500kg', '0-5m³'], + '依维柯': ['500-1500kg', '5-12m³'], + '平板': ['3000-15000kg', '20-60m³'], + '厢式': ['2000-10000kg', '15-45m³'], + '高栏车': ['3000-15000kg', '20-60m³'], + '集装箱': ['8000-20000kg', '40-80m³'], + '自卸': ['5000-12000kg', '20-45m³'], + '冷藏': ['2000-8000kg', '15-40m³'], + '保温': ['2000-6000kg', '15-30m³'], + '棉被车': ['2000-8000kg', '15-40m³'], + '爬梯车': ['5000-12000kg', '20-45m³'], + '飞翼车': ['3000-10000kg', '20-45m³'], + '高低板': ['8000-20000kg', '40-80m³'] } }, @@ -278,22 +344,7 @@ Page({ }); }, - // 选择车辆类型 - bindVehicleTypeChange: function (e) { - const index = e.detail.value; - this.setData({ - selectedVehicleTypeIndex: index, - 'vehicleInfo.type': this.data.vehicleTypes[index] - }); - }, - // 输入车辆信息 - bindVehicleInfoInput: function (e) { - const { key } = e.currentTarget.dataset; - this.setData({ - [`vehicleInfo.${key}`]: e.detail.value - }); - }, @@ -713,8 +764,7 @@ Page({ const params = { origin: this.data.origin, destination: this.data.destination, - goodsInfo: this.data.goodsInfo, - vehicleInfo: this.data.vehicleInfo + goodsInfo: this.data.goodsInfo }; // 调用API计算运费 @@ -760,15 +810,21 @@ Page({ volume: '', quantity: 1 }, - vehicleInfo: { - type: '', - capacity: '', - number: '', - driver: '', - phone: '' - }, - selectedVehicleTypeIndex: 0, - + // 运输参数 + transportModeIndex: 0, + packagingIndex: 0, + vehicleTypeIndex: 0, + truckLengthIndex: 5, + weight: '', + volume: '', + vehicleInfo: '面包车(1.4米): 0-500kg, 0-5m³', + // 计算结果 + showResult: false, + feeRange: '', + marketPrice: '', + complianceStatus: '', + isCompliant: true, + detailText: '', calculationResult: null }); }, @@ -782,18 +838,9 @@ Page({ origin: record.origin, destination: record.destination, goodsInfo: record.goodsInfo, - vehicleInfo: record.vehicleInfo || {}, transportMode: record.transportMode, calculationResult: record.result }); - - // 恢复车辆类型选择 - if (record.vehicleInfo && record.vehicleInfo.type) { - const vehicleTypeIndex = this.data.vehicleTypes.indexOf(record.vehicleInfo.type); - if (vehicleTypeIndex !== -1) { - this.setData({ selectedVehicleTypeIndex: vehicleTypeIndex }); - } - } }, // 删除历史记录 @@ -843,4 +890,1081 @@ Page({ } }, + // 运输模式选择 + bindTransportModeChange: function(e) { + this.setData({ + transportModeIndex: e.detail.value + }); + }, + + // 包装类型选择 + bindPackagingChange: function(e) { + this.setData({ + packagingIndex: e.detail.value + }); + }, + + // 车型选择 + bindVehicleTypeChange: function(e) { + const vehicleTypeIndex = e.detail.value; + const vehicleType = this.data.vehicleTypes[vehicleTypeIndex]; + + // 更新车长选项 + const truckLengths = this.data.vehicleLengthMap[vehicleType]; + + this.setData({ + vehicleTypeIndex: vehicleTypeIndex, + truckLengths: truckLengths, + truckLengthIndex: 0 + }); + + // 更新车型信息 + this.updateVehicleInfo(); + }, + + // 车长选择 + bindTruckLengthChange: function(e) { + this.setData({ + truckLengthIndex: e.detail.value + }); + + // 更新车型信息 + this.updateVehicleInfo(); + }, + + + + // 输入处理 + bindWeightInput: function(e) { + this.setData({ weight: e.detail.value }); + }, + + bindVolumeInput: function(e) { + this.setData({ volume: e.detail.value }); + }, + + // 更新车型信息 + updateVehicleInfo: function() { + const vehicleType = this.data.vehicleTypes[this.data.vehicleTypeIndex]; + const truckLength = this.data.truckLengths[this.data.truckLengthIndex]; + + if (this.data.baseInfo[vehicleType]) { + const [weightRange, volumeRange] = this.data.baseInfo[vehicleType]; + this.setData({ + vehicleInfo: `${vehicleType}(${truckLength}): ${weightRange}, ${volumeRange}` + }); + } else { + this.setData({ vehicleInfo: '请选择车型' }); + } + }, + + // 计算运费 + calculate: function(e) { + try { + // 获取输入参数 + const transportMode = this.data.transportModes[this.data.transportModeIndex]; + const weight = parseFloat(this.data.weight); + const volume = parseFloat(this.data.volume); + const packagingType = this.data.packagingIndex === 0 ? 'standard' : 'enhanced'; + const vehicleType = this.data.vehicleTypes[this.data.vehicleTypeIndex]; + const truckLength = this.data.truckLengths[this.data.truckLengthIndex]; + + // 验证输入 + if (isNaN(weight) || weight <= 0) { + wx.showToast({ title: '鸡蛋重量必须大于0', icon: 'none' }); + return; + } + if (isNaN(volume) || volume <= 0) { + wx.showToast({ title: '货物体积必须大于0', icon: 'none' }); + return; + } + + // 检查是否有出发地和目的地信息 + if (!this.data.origin.province || !this.data.destination.province) { + wx.showToast({ title: '请选择出发地和目的地', icon: 'none' }); + return; + } + + this.setData({ loading: true }); + + // 构建请求参数 + const params = { + origin: this.data.origin, + destination: this.data.destination, + goodsInfo: this.data.goodsInfo, + vehicleInfo: this.data.vehicleInfo + }; + + // 调用API计算运费,获取距离信息 + API.calculateFreight(params).then(res => { + this.setData({ loading: false }); + if (res.success) { + // 使用API返回的距离 + const distance = res.data.distance || 100; + + // 自动获取当前时间、天气情况和是否节假日 + const { weather, holiday, timePeriod } = this.getAutoData(); + + // 调用计算函数(使用默认等候时间40分钟) + const result = this.calculateEggShippingFee( + transportMode, distance, weight, volume, packagingType, vehicleType, truckLength, 40, weather, holiday, timePeriod + ); + + // 更新结果 + this.setData({ + showResult: true, + feeRange: `${result.feeMin} - ${result.feeMax}元`, + marketPrice: `${result.marketPrice}元`, + complianceStatus: result.compliance.isCompliant ? '✓ 运输方案合规' : '✗ 运输方案不合规', + isCompliant: result.compliance.isCompliant, + detailText: this.generateDetailText(result, transportMode, weight, volume, packagingType, 40), + calculationResult: res.data // 保存API返回的结果 + }); + + // 保存历史记录 + this.saveHistoryRecord({ + freight: (result.feeMin + result.feeMax) / 2, + distance: distance, + deliveryTime: Math.ceil(distance / 80), // 简单估算,假设平均速度80km/h + feeRange: `${result.feeMin} - ${result.feeMax}元`, + marketPrice: result.marketPrice, + complianceStatus: result.compliance.isCompliant ? '合规' : '不合规' + }); + } else { + wx.showToast({ + title: res.message || '计算失败,请稍后重试', + icon: 'none' + }); + } + }).catch(err => { + this.setData({ loading: false }); + console.error('计算运费失败:', err); + wx.showToast({ + title: '网络错误,请稍后重试', + icon: 'none' + }); + }); + + } catch (error) { + this.setData({ loading: false }); + wx.showToast({ title: '计算出错,请检查输入', icon: 'none' }); + console.error(error); + } + }, + + // 自动获取当前时间、天气情况和是否节假日 + getAutoData: function() { + // 获取当前时间 + const now = new Date(); + const hour = now.getHours(); + + // 确定时间段 + let timePeriod; + if (hour >= 6 && hour < 12) { + timePeriod = '上午'; + } else if (hour >= 12 && hour < 18) { + timePeriod = '下午'; + } else { + timePeriod = '晚上'; + } + + // 简单判断是否节假日(这里只是示例,实际需要更复杂的逻辑) + // 这里假设周末为节假日 + const dayOfWeek = now.getDay(); + const holiday = dayOfWeek === 0 || dayOfWeek === 6; + + // 天气情况(这里只是示例,实际需要调用天气API) + // 这里使用默认值晴朗 + const weather = '晴朗'; + + return { weather, holiday, timePeriod }; + }, + + // 生成详细文本 + generateDetailText: function(result, transportMode, weight, volume, packagingType, waitTime) { + let text = `费用明细:\n`; + text += ` 最低运费: ${result.feeMin}元\n`; + text += ` 最高运费: ${result.feeMax}元\n`; + text += ` 平均运费: ${result.feeAvg}元\n\n`; + + if (result.details.breakdown) { + text += `费用组成:\n`; + for (const [key, value] of Object.entries(result.details.breakdown)) { + text += ` ${key}: ${value}元\n`; + } + text += `\n`; + } + + text += `运输详情:\n`; + text += ` 运输模式: ${transportMode}\n`; + text += ` 车型: ${result.details.vehicleType}\n`; + text += ` 包装类型: ${packagingType === 'standard' ? '标准包装' : '加强包装'}\n`; + text += ` 实际重量: ${weight}公斤\n`; + text += ` 体积: ${volume}立方米\n`; + if (result.details.volumeWeight) { + text += ` 体积重量: ${result.details.volumeWeight}公斤\n`; + } + if (result.details.chargeWeight) { + text += ` 计费重量: ${result.details.chargeWeight}公斤\n`; + } + text += ` 预计等候时间: ${waitTime}分钟\n`; + if (result.details.estimatedPackages) { + text += ` 估算包装数量: ${result.details.estimatedPackages}个\n`; + } + // 添加运输距离信息 + text += ` 运输距离: ${result.distance || 0}公里\n`; + text += `\n`; + + if (result.details.marketFactor) { + text += `市场因素:\n`; + text += ` 天气状况: ${result.details.weather}\n`; + text += ` 是否节假日: ${result.details.holiday}\n`; + text += ` 时间段: ${result.details.timePeriod}\n`; + text += ` 市场因素系数: ${result.details.marketFactor}\n\n`; + } + + text += `市场参考:\n`; + text += ` 市场参考价: ${result.marketPrice}元\n`; + text += ` 价格合理性: ${result.priceReasonability}\n\n`; + + if (!result.compliance.isCompliant) { + text += `不合规原因:\n`; + for (const issue of result.compliance.issues) { + text += ` - ${issue}\n`; + } + } + + return text; + }, + + // 计算鸡蛋运输运费 + calculateEggShippingFee: function(transportMode, distance, weight, volume, packagingType, vehicleType, truckLength, waitTime, weather, holiday, timePeriod) { + // 计算市场因素系数 + const calculateMarketFactor = (weather, holiday, timePeriod) => { + let factor = 1.0; + + // 天气因素 + const weatherFactors = { + '晴朗': 1.0, + '多云': 1.05, + '小雨': 1.1, + '中雨': 1.2, + '大雨': 1.3, + '暴雨': 1.5, + '雪': 1.4, + '雾': 1.3 + }; + factor *= weatherFactors[weather] || 1.0; + + // 节假日因素 + if (holiday) { + factor *= 1.2; + } + + // 时间段因素 + const timeFactors = { + '上午': 1.0, + '下午': 1.1, + '晚上': 1.2 + }; + factor *= timeFactors[timePeriod] || 1.0; + + return factor; + }; + + // 计算体积重量(物流行业标准:1立方米 = 333公斤) + const volumeWeight = volume * 333; + // 计费重量取实际重量和体积重量的较大值 + const chargeWeight = Math.max(weight, volumeWeight); + + // 计算市场因素系数 + const marketFactor = calculateMarketFactor(weather, holiday, timePeriod); + + // 车长系数计算 + const getTruckLengthFactor = (truckLength) => { + try { + const length = parseFloat(truckLength.replace('米', '')); + if (length <= 3.8) { + return 0.8; + } else if (length <= 4.2) { + return 0.9; + } else if (length <= 5) { + return 1.0; + } else if (length <= 6.8) { + return 1.1; + } else if (length <= 7.7) { + return 1.2; + } else if (length <= 9.6) { + return 1.3; + } else if (length <= 13) { + return 1.4; + } else { + return 1.5; + } + } catch { + return 1.0; + } + }; + + // 获取车长系数 + const truckLengthFactor = getTruckLengthFactor(truckLength); + + // 合规性检查参数 + const complianceParams = { + maxWeightPerVehicle: { + '面包车': 500, + '依维柯': 1500, + '平板': 15000, + '厢式': 10000, + '高栏车': 15000, + '集装箱': 20000, + '自卸': 12000, + '冷藏': 8000, + '保温': 6000, + '棉被车': 8000, + '爬梯车': 12000, + '飞翼车': 10000, + '高低板': 20000 + }, + maxVolumePerVehicle: { + '面包车': 5, + '依维柯': 12, + '平板': 60, + '厢式': 45, + '高栏车': 60, + '集装箱': 80, + '自卸': 45, + '冷藏': 40, + '保温': 30, + '棉被车': 40, + '爬梯车': 45, + '飞翼车': 45, + '高低板': 80 + }, + weightPerPackage: 20, + volumePerPackage: 0.2, + minDistance: 1, + maxDistance: 2000 + }; + + // 合规性检查 + const compliance = { + isCompliant: true, + issues: [] + }; + + // 检查距离 + if (distance < complianceParams.minDistance) { + compliance.isCompliant = false; + compliance.issues.push(`运输距离必须大于等于${complianceParams.minDistance}公里`); + } + if (distance > complianceParams.maxDistance) { + compliance.isCompliant = false; + compliance.issues.push(`运输距离不能超过${complianceParams.maxDistance}公里`); + } + + // 检查车型载重 + if (complianceParams.maxWeightPerVehicle[vehicleType]) { + if (chargeWeight > complianceParams.maxWeightPerVehicle[vehicleType]) { + compliance.isCompliant = false; + compliance.issues.push(`${vehicleType}载重不能超过${complianceParams.maxWeightPerVehicle[vehicleType]}公斤`); + } + } + + // 检查车型体积 + if (complianceParams.maxVolumePerVehicle[vehicleType]) { + if (volume > complianceParams.maxVolumePerVehicle[vehicleType]) { + compliance.isCompliant = false; + compliance.issues.push(`${vehicleType}体积不能超过${complianceParams.maxVolumePerVehicle[vehicleType]}立方米`); + } + } + + // 检查包装要求 + const estimatedPackages = Math.max(weight / complianceParams.weightPerPackage, volume / complianceParams.volumePerPackage); + if (estimatedPackages > 100) { + compliance.isCompliant = false; + compliance.issues.push('包装数量过多,建议分批运输'); + } + + // 检查是否合规 + const isCompliant = compliance.isCompliant; + + // 计算运费区间 + let feeMin, feeMax, breakdown; + if (transportMode === '货拉拉') { + // 货拉拉收费标准 + [feeMin, feeMax, breakdown] = this.calculateHuolalaFee(distance, vehicleType, truckLength, waitTime, packagingType); + } else if (transportMode === '运满满') { + // 运满满收费标准 + [feeMin, feeMax, breakdown] = this.calculateYunmanmanFee(distance, weight, volume, vehicleType, truckLength); + } else { + // 零担拼车收费标准 + [feeMin, feeMax, breakdown] = this.calculateLessThanTruckloadFee(distance, chargeWeight, packagingType, truckLength); + } + + // 如果不合规(货物超过车型限制),调整运费 + if (!isCompliant) { + // 检查是否是载重或体积超过限制 + const weightExceeded = weight > (complianceParams.maxWeightPerVehicle[vehicleType] || Infinity); + const volumeExceeded = volume > (complianceParams.maxVolumePerVehicle[vehicleType] || Infinity); + + if (weightExceeded || volumeExceeded) { + // 取重量和体积超出比例的最大值 + let maxExceedRatio = 1.0; + + if (weightExceeded) { + const maxWeight = complianceParams.maxWeightPerVehicle[vehicleType] || 1; + const weightRatio = weight / maxWeight; + maxExceedRatio = Math.max(maxExceedRatio, weightRatio); + } + + if (volumeExceeded) { + const maxVolume = complianceParams.maxVolumePerVehicle[vehicleType] || 1; + const volumeRatio = volume / maxVolume; + maxExceedRatio = Math.max(maxExceedRatio, volumeRatio); + } + + // 根据最大超出比例计算调整系数 + let exceedFactor; + if (maxExceedRatio > 5) { + exceedFactor = 2.0; + } else if (maxExceedRatio > 3) { + exceedFactor = 1.8; + } else if (maxExceedRatio > 2) { + exceedFactor = 1.6; + } else { + exceedFactor = 1.4; + } + + // 应用超出系数 + feeMin *= exceedFactor; + feeMax *= exceedFactor; + } + } + + // 应用市场因素系数 + feeMin *= marketFactor; + feeMax *= marketFactor; + + // 计算平均运费 + const feeAvg = (feeMin + feeMax) / 2; + + // 计算市场参考价 + let marketPrice = this.calculateMarketPrice(transportMode, distance, weight, volume, vehicleType, truckLength); + + // 如果不合规(货物超过车型限制),调整市场参考价 + if (!isCompliant) { + // 检查是否是载重或体积超过限制 + const weightExceeded = weight > (complianceParams.maxWeightPerVehicle[vehicleType] || Infinity); + const volumeExceeded = volume > (complianceParams.maxVolumePerVehicle[vehicleType] || Infinity); + + if (weightExceeded || volumeExceeded) { + // 取重量和体积超出比例的最大值 + let maxExceedRatio = 1.0; + + if (weightExceeded) { + const maxWeight = complianceParams.maxWeightPerVehicle[vehicleType] || 1; + const weightRatio = weight / maxWeight; + maxExceedRatio = Math.max(maxExceedRatio, weightRatio); + } + + if (volumeExceeded) { + const maxVolume = complianceParams.maxVolumePerVehicle[vehicleType] || 1; + const volumeRatio = volume / maxVolume; + maxExceedRatio = Math.max(maxExceedRatio, volumeRatio); + } + + // 根据最大超出比例计算调整系数 + let exceedFactor; + if (maxExceedRatio > 5) { + exceedFactor = 2.0; + } else if (maxExceedRatio > 3) { + exceedFactor = 1.8; + } else if (maxExceedRatio > 2) { + exceedFactor = 1.6; + } else { + exceedFactor = 1.4; + } + + // 应用超出系数 + marketPrice *= exceedFactor; + } + } + + // 应用市场因素系数 + marketPrice *= marketFactor; + + // 评估价格合理性 + let priceReasonability; + if (feeAvg < marketPrice * 0.8) { + priceReasonability = '价格偏低,可能存在服务质量风险'; + } else if (feeAvg > marketPrice * 1.2) { + priceReasonability = '价格偏高,建议重新议价'; + } else { + priceReasonability = '价格合理,符合市场行情'; + } + + // 详细信息 + const details = { + breakdown: breakdown, + volumeWeight: Math.round(volumeWeight * 100) / 100, + chargeWeight: Math.round(chargeWeight * 100) / 100, + estimatedPackages: Math.round(estimatedPackages), + vehicleType: vehicleType, + packagingType: packagingType, + marketFactor: Math.round(marketFactor * 100) / 100, + weather: weather, + holiday: holiday ? '是' : '否', + timePeriod: timePeriod + }; + + return { + feeMin: Math.round(feeMin * 100) / 100, + feeMax: Math.round(feeMax * 100) / 100, + feeAvg: Math.round(feeAvg * 100) / 100, + marketPrice: Math.round(marketPrice * 100) / 100, + priceReasonability: priceReasonability, + compliance: compliance, + details: details, + distance: distance // 添加距离信息 + }; + }, + + // 货拉拉运费计算 + calculateHuolalaFee: function(distance, vehicleType, truckLength, waitTime, packagingType) { + // 车长系数计算 + const getTruckLengthFactor = (truckLength) => { + try { + const length = parseFloat(truckLength.replace('米', '')); + if (length <= 3.8) { + return 0.8; + } else if (length <= 4.2) { + return 0.9; + } else if (length <= 5) { + return 1.0; + } else if (length <= 6.8) { + return 1.1; + } else if (length <= 7.7) { + return 1.2; + } else if (length <= 9.6) { + return 1.3; + } else if (length <= 13) { + return 1.4; + } else { + return 1.5; + } + } catch { + return 1.0; + } + }; + + // 货拉拉收费标准 + const rates = { + '面包车': { + startFee: 30, + startDistance: 5, + unitPrice: 2.8, + extraStart: 6, + freeWaitTime: 40, + waitFee: 5, + waitInterval: 10 + }, + '依维柯': { + startFee: 65, + startDistance: 5, + unitPrice: 4, + extraStart: 6, + freeWaitTime: 40, + waitFee: 5, + waitInterval: 10 + }, + '平板': { + startFee: 200, + startDistance: 10, + unitPrice: 4.8, + extraStart: 11, + freeWaitTime: 60, + waitFee: 20, + waitInterval: 15 + }, + '厢式': { + startFee: 220, + startDistance: 10, + unitPrice: 5.0, + extraStart: 11, + freeWaitTime: 60, + waitFee: 20, + waitInterval: 15 + }, + '高栏车': { + startFee: 180, + startDistance: 10, + unitPrice: 4.5, + extraStart: 11, + freeWaitTime: 60, + waitFee: 25, + waitInterval: 15 + }, + '冷藏': { + startFee: 280, + startDistance: 10, + unitPrice: 6.0, + extraStart: 11, + freeWaitTime: 60, + waitFee: 30, + waitInterval: 15 + }, + '保温': { + startFee: 250, + startDistance: 10, + unitPrice: 5.5, + extraStart: 11, + freeWaitTime: 60, + waitFee: 25, + waitInterval: 15 + }, + '棉被车': { + startFee: 230, + startDistance: 10, + unitPrice: 5.2, + extraStart: 11, + freeWaitTime: 60, + waitFee: 22, + waitInterval: 15 + }, + '飞翼车': { + startFee: 240, + startDistance: 10, + unitPrice: 5.3, + extraStart: 11, + freeWaitTime: 60, + waitFee: 23, + waitInterval: 15 + } + }; + + const rate = rates[vehicleType] || rates['面包车']; + + // 获取车长系数 + const truckLengthFactor = getTruckLengthFactor(truckLength); + + // 计算距离费用 + let distanceFee; + if (distance <= rate.startDistance) { + distanceFee = rate.startFee; + } else { + const extraDistance = distance - rate.startDistance; + distanceFee = rate.startFee + extraDistance * rate.unitPrice; + } + + // 计算等候费用 + let waitFee; + if (waitTime > rate.freeWaitTime) { + const extraWaitTime = waitTime - rate.freeWaitTime; + waitFee = Math.ceil(extraWaitTime / rate.waitInterval) * rate.waitFee; + } else { + waitFee = 0; + } + + // 应用车长系数 + distanceFee *= truckLengthFactor; + waitFee *= truckLengthFactor; + + // 计算包装费用 + const packagingFee = packagingType === 'standard' ? 1 : 2; + + // 计算最低和最高运费(考虑价格波动10%) + const baseFee = distanceFee + waitFee; + const feeMin = baseFee * 0.9; + const feeMax = baseFee * 1.1; + + // 费用明细 + const breakdown = { + '起步费': Math.round(rate.startFee * truckLengthFactor * 100) / 100, + '距离费': Math.round((distanceFee - rate.startFee * truckLengthFactor) * 100) / 100, + '等候费': Math.round(waitFee * 100) / 100, + '包装费': packagingFee + }; + + return [feeMin, feeMax, breakdown]; + }, + + // 运满满运费计算 + calculateYunmanmanFee: function(distance, weight, volume, vehicleType, truckLength) { + // 车长系数计算 + const getTruckLengthFactor = (truckLength) => { + try { + const length = parseFloat(truckLength.replace('米', '')); + if (length <= 3.8) { + return 0.8; + } else if (length <= 4.2) { + return 0.9; + } else if (length <= 5) { + return 1.0; + } else if (length <= 6.8) { + return 1.1; + } else if (length <= 7.7) { + return 1.2; + } else if (length <= 9.6) { + return 1.3; + } else if (length <= 13) { + return 1.4; + } else { + return 1.5; + } + } catch { + return 1.0; + } + }; + + // 运满满收费标准 + const rates = { + '面包车': { + startFee: 80, + startDistance: 10, + unitPriceMin: 1.0, + unitPriceMax: 1.3 + }, + '依维柯': { + startFee: 160, + startDistance: 10, + unitPriceMin: 1.5, + unitPriceMax: 1.8 + }, + '平板': { + startFee: 300, + startDistance: 10, + unitPriceMin: 2.5, + unitPriceMax: 2.9 + }, + '厢式': { + startFee: 220, + startDistance: 10, + unitPriceMin: 2.1, + unitPriceMax: 2.4 + }, + '高栏车': { + startFee: 200, + startDistance: 10, + unitPriceMin: 2.5, + unitPriceMax: 2.8 + }, + '集装箱': { + startFee: 300, + startDistance: 10, + unitPriceMin: 2.8, + unitPriceMax: 3.1 + }, + '自卸': { + startFee: 250, + startDistance: 10, + unitPriceMin: 2.2, + unitPriceMax: 2.5 + }, + '冷藏': { + startFee: 300, + startDistance: 10, + unitPriceMin: 2.5, + unitPriceMax: 2.8 + }, + '保温': { + startFee: 280, + startDistance: 10, + unitPriceMin: 2.3, + unitPriceMax: 2.6 + }, + '棉被车': { + startFee: 260, + startDistance: 10, + unitPriceMin: 2.2, + unitPriceMax: 2.5 + }, + '爬梯车': { + startFee: 280, + startDistance: 10, + unitPriceMin: 2.3, + unitPriceMax: 2.6 + }, + '飞翼车': { + startFee: 270, + startDistance: 10, + unitPriceMin: 2.2, + unitPriceMax: 2.5 + }, + '高低板': { + startFee: 350, + startDistance: 10, + unitPriceMin: 2.8, + unitPriceMax: 3.1 + } + }; + + const rate = rates[vehicleType] || rates['面包车']; + + // 获取车长系数 + const truckLengthFactor = getTruckLengthFactor(truckLength); + + // 计算距离费用 + let feeMin, feeMax; + if (distance <= rate.startDistance) { + feeMin = rate.startFee; + feeMax = rate.startFee; + } else { + const extraDistance = distance - rate.startDistance; + feeMin = rate.startFee + extraDistance * rate.unitPriceMin; + feeMax = rate.startFee + extraDistance * rate.unitPriceMax; + } + + // 应用车长系数 + feeMin *= truckLengthFactor; + feeMax *= truckLengthFactor; + + // 根据货物重量和体积调整价格 + if (weight > 1000 || volume > 10) { + feeMin *= 1.1; + feeMax *= 1.1; + } + + // 添加平台佣金(运费的5%左右) + feeMin *= 1.05; + feeMax *= 1.05; + + // 费用明细 + const breakdown = { + '起步费': rate.startFee, + '距离费': Math.round((feeMin / 1.05 - rate.startFee) * 100) / 100, + '平台佣金': Math.round((feeMin - feeMin / 1.05) * 100) / 100 + }; + + return [feeMin, feeMax, breakdown]; + }, + + // 零担拼车运费计算 + calculateLessThanTruckloadFee: function(distance, chargeWeight, packagingType, truckLength) { + // 车长系数计算 + const getTruckLengthFactor = (truckLength) => { + try { + const length = parseFloat(truckLength.replace('米', '')); + if (length <= 3.8) { + return 0.9; + } else if (length <= 4.2) { + return 0.95; + } else if (length <= 5) { + return 1.0; + } else if (length <= 6.8) { + return 1.05; + } else if (length <= 7.7) { + return 1.1; + } else if (length <= 9.6) { + return 1.15; + } else if (length <= 13) { + return 1.2; + } else { + return 1.25; + } + } catch { + return 1.0; + } + }; + + // 零担拼车收费标准 + const perKgPer1000kmMin = 0.3; + const perKgPer1000kmMax = 0.5; + + // 包装费用 + const packagingFeeMin = packagingType === 'standard' ? 0.15 : 0.4; + const packagingFeeMax = packagingType === 'standard' ? 0.25 : 0.6; + + // 易碎品加价 + const fragileFactorMin = 1.02; + const fragileFactorMax = 1.08; + + // 计算距离因子 + const distanceFactor = distance / 1000; + + // 获取车长系数 + const truckLengthFactor = getTruckLengthFactor(truckLength); + + // 计算运费 + let freightFeeMin = chargeWeight * perKgPer1000kmMin * distanceFactor; + let freightFeeMax = chargeWeight * perKgPer1000kmMax * distanceFactor; + + const packagingFeeTotalMin = chargeWeight * packagingFeeMin; + const packagingFeeTotalMax = chargeWeight * packagingFeeMax; + + // 计算易碎品附加费 + const fragileFeeMin = (freightFeeMin + packagingFeeTotalMin) * (fragileFactorMin - 1); + const fragileFeeMax = (freightFeeMax + packagingFeeTotalMax) * (fragileFactorMax - 1); + + // 总运费 + let feeMin = freightFeeMin + packagingFeeTotalMin + fragileFeeMin; + let feeMax = freightFeeMax + packagingFeeTotalMax + fragileFeeMax; + + // 应用车长系数 + feeMin *= truckLengthFactor; + feeMax *= truckLengthFactor; + + // 费用明细 + const breakdown = { + '运费': Math.round(freightFeeMin * 100) / 100, + '包装费': Math.round(packagingFeeTotalMin * 100) / 100, + '易碎品附加费': Math.round(fragileFeeMin * 100) / 100 + }; + + return [feeMin, feeMax, breakdown]; + }, + + // 计算市场参考价 + calculateMarketPrice: function(transportMode, distance, weight, volume, vehicleType, truckLength) { + // 车长系数计算 + const getTruckLengthFactor = (truckLength) => { + try { + const length = parseFloat(truckLength.replace('米', '')); + if (length <= 3.8) { + return 0.8; + } else if (length <= 4.2) { + return 0.9; + } else if (length <= 5) { + return 1.0; + } else if (length <= 6.8) { + return 1.1; + } else if (length <= 7.7) { + return 1.2; + } else if (length <= 9.6) { + return 1.3; + } else if (length <= 13) { + return 1.4; + } else { + return 1.5; + } + } catch { + return 1.0; + } + }; + + // 获取车长系数 + const truckLengthFactor = getTruckLengthFactor(truckLength); + + let marketPrice; + if (transportMode === '货拉拉') { + // 基于货拉拉真实收费标准 + const rates = { + '面包车': 30 + Math.max(0, distance - 5) * 2.8, + '依维柯': 65 + Math.max(0, distance - 5) * 4, + '平板': 200 + Math.max(0, distance - 10) * 4.8, + '厢式': 220 + Math.max(0, distance - 10) * 5.0, + '高栏车': 180 + Math.max(0, distance - 10) * 4.5, + '冷藏': 280 + Math.max(0, distance - 10) * 6.0, + '保温': 250 + Math.max(0, distance - 10) * 5.5, + '棉被车': 230 + Math.max(0, distance - 10) * 5.2, + '飞翼车': 240 + Math.max(0, distance - 10) * 5.3 + }; + marketPrice = rates[vehicleType] || rates['面包车']; + // 应用车长系数 + marketPrice *= truckLengthFactor; + } else if (transportMode === '运满满') { + // 基于运满满真实收费标准 + const rates = { + '面包车': { + startFee: 80, + startDistance: 10, + unitPrice: 1.15 + }, + '依维柯': { + startFee: 160, + startDistance: 10, + unitPrice: 1.65 + }, + '平板': { + startFee: 300, + startDistance: 10, + unitPrice: 2.7 + }, + '厢式': { + startFee: 220, + startDistance: 10, + unitPrice: 2.25 + }, + '高栏车': { + startFee: 200, + startDistance: 10, + unitPrice: 2.65 + }, + '集装箱': { + startFee: 300, + startDistance: 10, + unitPrice: 2.95 + }, + '自卸': { + startFee: 250, + startDistance: 10, + unitPrice: 2.35 + }, + '冷藏': { + startFee: 300, + startDistance: 10, + unitPrice: 2.65 + }, + '保温': { + startFee: 280, + startDistance: 10, + unitPrice: 2.45 + }, + '棉被车': { + startFee: 260, + startDistance: 10, + unitPrice: 2.35 + }, + '爬梯车': { + startFee: 280, + startDistance: 10, + unitPrice: 2.45 + }, + '飞翼车': { + startFee: 270, + startDistance: 10, + unitPrice: 2.35 + }, + '高低板': { + startFee: 350, + startDistance: 10, + unitPrice: 2.95 + } + }; + + const rate = rates[vehicleType] || rates['面包车']; + + if (distance <= rate.startDistance) { + marketPrice = rate.startFee; + } else { + const extraDistance = distance - rate.startDistance; + marketPrice = rate.startFee + extraDistance * rate.unitPrice; + } + + // 根据货物重量和体积调整价格 + if (weight > 1000 || volume > 10) { + marketPrice *= 1.1; + } + + // 添加平台佣金 + marketPrice *= 1.05; + + // 应用车长系数 + marketPrice *= truckLengthFactor; + } else { + // 零担拼车市场参考价 + const chargeWeight = Math.max(weight, volume * 333); + marketPrice = chargeWeight * 0.4 * (distance / 1000) * 1.05; // 包含易碎品附加费 + + // 应用车长系数 + marketPrice *= truckLengthFactor; + } + + // 确保最低价格 + const minPrice = 100; // 最低起步价 + marketPrice = Math.max(minPrice, marketPrice); + + return marketPrice; + } + }); \ No newline at end of file diff --git a/pages/freight-calculator/index.wxml b/pages/freight-calculator/index.wxml index 8aa48b0..ce2b09b 100644 --- a/pages/freight-calculator/index.wxml +++ b/pages/freight-calculator/index.wxml @@ -84,77 +84,74 @@ - + + + - 物流车辆信息 - + 运输参数 + + - 车辆类型 - + 运输模式 + - {{vehicleTypes[selectedVehicleTypeIndex]}} + {{transportModes[transportModeIndex]}} + + + + - 载重能力 (吨) - + 鸡蛋重量(公斤) + + + - 车辆编号 - + 货物体积(立方米) + + + - 司机姓名 - + 包装类型 + + + {{packagingTypes[packagingIndex]}} + + + + - 联系电话 - - - - - - - - 物流人员信息 - - 我们拥有专业的物流团队,为您提供高效、安全的运输服务。以下是我们的物流专家,他们具有丰富的行业经验和专业知识,能够为您的货物运输提供专业的解决方案。 - - - - - - {{item.name.charAt(0)}} - - - {{item.name}} - {{item.position}} - - {{item.experience}}年物流经验 - 认证物流师 - 专业配送 - - - - - - {{item.description || '拥有丰富的物流行业经验,熟悉各种货物的运输要求和流程,能够为客户提供专业的物流解决方案和优质的服务。'}} + 车型 + + + {{vehicleTypes[vehicleTypeIndex]}} - - - - 联系电话: - {{item.phone}} + + + + + + 车长 + + + {{truckLengths[truckLengthIndex]}} - - + - - 暂无物流人员信息 + + + + 车型载重/体积参考 + {{vehicleInfo}} + + + @@ -162,7 +159,7 @@ - + + + + 暂无物流人员信息 + + + diff --git a/pages/freight-calculator/index.wxss b/pages/freight-calculator/index.wxss index a5e0603..f49973d 100644 --- a/pages/freight-calculator/index.wxss +++ b/pages/freight-calculator/index.wxss @@ -642,6 +642,31 @@ color: #ff4d4f; } +/* 车型信息样式 */ +.vehicle-info { + font-size: 20rpx; + color: #999; + margin-top: 4rpx; + line-height: 1.4; +} + +/* 详细费用明细样式 */ +.detail-section { + margin-top: 16rpx; + background-color: #f8f9fa; + border-radius: 8rpx; + padding: 16rpx; + border-left: 4rpx solid #e3f2fd; +} + +.detail-text { + font-size: 20rpx; + color: #666; + line-height: 1.5; + white-space: pre-wrap; + word-break: break-all; +} + /* 动画效果 */ @keyframes fadeInUp { from { diff --git a/pages/goods-detail/goods-detail.js b/pages/goods-detail/goods-detail.js index 69156d3..5429069 100644 --- a/pages/goods-detail/goods-detail.js +++ b/pages/goods-detail/goods-detail.js @@ -481,10 +481,10 @@ function processWeightAndQuantityData(weightSpecString, quantityString, specStri } } display = `${weightSpecDisplay}————已售${soldQuantity}件`; - } else if (weightSpecDisplay.includes('剩余售空件')) { - // 替换已有的"剩余售空件"为"已售多少件" - const soldQuantity = weightSpecDisplay.match(/剩余售空(\d+)件/)?.[1] || 0; - display = `${weightSpecDisplay.replace('剩余售空件', `已售${soldQuantity}件`)}`; + } else if (weightSpecDisplay.includes('剩余售空件') || weightSpecDisplay.includes('已售售空件')) { + // 替换已有的"剩余售空件"或"已售售空件"为"已售多少件" + const soldQuantity = weightSpecDisplay.match(/(\d+)件/)?.[1] || 0; + display = weightSpecDisplay.replace(/(剩余售空|已售售空)件/, `已售${soldQuantity}件`); } else { if (price) { display = `${weightSpecDisplay}【${quantity}件】¥${price}元`; @@ -494,9 +494,9 @@ function processWeightAndQuantityData(weightSpecString, quantityString, specStri } } else if (weightSpecDisplay) { // 处理只包含净重信息的情况 - if (weightSpecDisplay.includes('剩余售空件')) { - const soldQuantity = weightSpecDisplay.match(/剩余售空(\d+)件/)?.[1] || 0; - display = weightSpecDisplay.replace('剩余售空件', `已售${soldQuantity}件`); + if (weightSpecDisplay.includes('剩余售空件') || weightSpecDisplay.includes('已售售空件')) { + const soldQuantity = weightSpecDisplay.match(/(\d+)件/)?.[1] || 0; + display = weightSpecDisplay.replace(/(剩余售空|已售售空)件/, `已售${soldQuantity}件`); } else if (weightSpecDisplay.includes('售空')) { // 尝试从 weightSpecDisplay 中提取库存数量 const quantityMatch = weightSpecDisplay.match(/(\d+)/); @@ -525,13 +525,13 @@ function processWeightAndQuantityData(weightSpecString, quantityString, specStri // 处理 quantity 字段,确保售空时也有正确的数量值 let processedQuantity = quantity; - if (quantity === '售空' || weightSpecDisplay.includes('售空')) { + if (quantity === '售空' || weightSpecDisplay.includes('售空') || weightSpecDisplay.includes('已售售空件') || weightSpecDisplay.includes('剩余售空件')) { // 尝试从 weightSpecDisplay 中提取数字 const quantityMatch = weightSpecDisplay.match(/(\d+)/); if (quantityMatch) { processedQuantity = quantityMatch[1]; - } else if (quantity !== '售空') { - // 如果 quantity 不是 '售空',则保持原值 + } else if (quantity !== '售空' && quantity !== '') { + // 如果 quantity 不是 '售空' 且不为空,则保持原值 processedQuantity = quantity; } else { // 尝试从 display 字段中提取数字 @@ -2171,6 +2171,7 @@ Page({ const isSoldOut = product.status === 'sold_out' || product.status === 'sold' || product.status === 'out_of_stock' || + product.status === '售空' || (product.supplyStatus && product.supplyStatus.includes('售空')); console.log('===== 售空判断详细分析 ====='); @@ -2190,6 +2191,7 @@ Page({ console.log('当前isSoldOut值:', isSoldOut); console.log('即将执行的条件分支...'); + // 处理售空状态和非售空状态 if (isSoldOut) { // 售空状态的商品,显示规格信息和价格 if (weightSpecString) { @@ -2207,23 +2209,35 @@ Page({ } else if (priceString) { priceArray = [String(priceString)]; } + // 处理数量数组 + let quantityArray = []; + if (quantityString && typeof quantityString === 'string') { + quantityArray = quantityString.split(/[,,、]/).map(item => item.trim()).filter(item => item); + } else if (quantityString) { + quantityArray = [String(quantityString)]; + } weightQuantityData = weightSpecArray.map((spec, index) => { const price = priceArray[index] || ''; + // 使用实际的quantity字段,而不是从spec中提取 + const soldQuantity = quantityArray[index] || product.quantity || 0; return { weightSpec: spec.includes('毛重') ? spec : `毛重${spec}`, - quantity: '售空', + quantity: soldQuantity, price: price, display: price ? - `${spec.includes('毛重') ? spec : `毛重${spec}`}【售空】¥${price}元` : - `${spec.includes('毛重') ? spec : `毛重${spec}`}【售空】` + `${spec.includes('毛重') ? spec : `毛重${spec}`}【已售${soldQuantity}件】¥${price}元` : + `${spec.includes('毛重') ? spec : `毛重${spec}`}【已售${soldQuantity}件】`, + isSoldOut: true }; }); } else { // 如果没有规格信息,则显示默认的售空信息 + const soldQuantity = product.quantity || 0; weightQuantityData = [{ weightSpec: '规格信息', - quantity: '售空', - display: '规格信息【售空】' + quantity: soldQuantity, + display: `规格信息【已售${soldQuantity}件】`, + isSoldOut: true }]; } console.log('✓ 售空分支执行: 显示规格信息和价格'); @@ -2246,6 +2260,12 @@ Page({ } weightQuantityData = processWeightAndQuantityData(weightSpecString, quantityString, '', priceString, specStatusString, specPriceChanges); + // 为非售空状态的商品添加isSoldOut标志 + weightQuantityData = weightQuantityData.map(item => ({ + ...item, + isSoldOut: false + })); + console.log('× 非售空分支结果:', weightQuantityData); } diff --git a/pages/goods-detail/goods-detail.wxml b/pages/goods-detail/goods-detail.wxml index cda32d2..a94bc14 100644 --- a/pages/goods-detail/goods-detail.wxml +++ b/pages/goods-detail/goods-detail.wxml @@ -131,7 +131,7 @@ 已下架 - 剩余{{item.quantity}}件 + 剩余{{item.quantity}}件 已售{{item.quantity || 0}}件