diff --git a/app.json b/app.json
index f26ed92..aede080 100644
--- a/app.json
+++ b/app.json
@@ -24,7 +24,7 @@
"pages/company/company",
"pages/qrcode/index",
"pages/collection/index",
- "pages/compare_price/index"
+ "pages/freight-calculator/index"
],
"subpackages": [
{
diff --git a/pages/freight-calculator/index.js b/pages/freight-calculator/index.js
new file mode 100644
index 0000000..27d29d2
--- /dev/null
+++ b/pages/freight-calculator/index.js
@@ -0,0 +1,846 @@
+// pages/freight-calculator/index.js
+const API = require('../../utils/api.js');
+
+Page({
+ data: {
+ // 出发地信息
+ origin: {
+ province: '',
+ city: '',
+ district: '',
+ detail: ''
+ },
+ // 目的地信息
+ destination: {
+ province: '',
+ city: '',
+ district: '',
+ detail: ''
+ },
+ // 货物信息
+ goodsInfo: {
+ weight: '', // 重量(kg)
+ volume: '', // 体积(m³)
+ quantity: 1, // 数量
+ type: 'egg' // 货物类型,默认为鸡蛋
+ },
+ // 物流车辆信息
+ vehicleInfo: {
+ type: '', // 车辆类型
+ capacity: '', // 载重能力(吨)
+ number: '', // 车辆编号
+ driver: '', // 司机姓名
+ phone: '' // 联系电话
+ },
+ // 物流人员信息
+ logisticsPersonnel: [
+ {
+ id: 1,
+ name: '张三',
+ position: '物流经理',
+ experience: 5,
+ phone: '13800138000'
+ },
+ {
+ id: 2,
+ name: '李四',
+ position: '配送专员',
+ experience: 3,
+ phone: '13900139000'
+ },
+ {
+ id: 3,
+ name: '王五',
+ position: '调度员',
+ experience: 2,
+ phone: '13700137000'
+ }
+ ],
+ // 车辆类型列表
+ vehicleTypes: ['厢式货车', '冷藏车', '平板车', '高栏车', '自卸车'],
+ // 选中的车辆类型索引
+ selectedVehicleTypeIndex: 0,
+ // 选中的货源信息
+ selectedGoods: {},
+ // 选中的规格索引
+ selectedSpecIndex: 0,
+ // 计算结果
+ calculationResult: null,
+ // 地址选择器状态
+ showOriginPicker: false,
+ showDestinationPicker: false,
+ // 加载状态
+ loading: false,
+ // 历史记录
+ historyRecords: [],
+ // 地址选择器数据
+ regionList: {
+ provinces: [],
+ cities: [],
+ districts: []
+ },
+ // 地址选择器当前选中
+ pickerValues: {
+ origin: [0, 0, 0],
+ destination: [0, 0, 0]
+ }
+ },
+
+ onLoad: function (options) {
+ // 页面加载时的初始化逻辑
+ console.log('物流运费估算页面加载,options:', options);
+
+ // 加载历史记录
+ this.loadHistoryRecords();
+
+ // 初始化地址选择器数据
+ this.initRegionData();
+
+ // 如果从商品详情页跳转过来,获取商品信息
+ if (options.goodsId) {
+ this.loadGoodsInfo(options.goodsId);
+ }
+
+ // 如果直接传递了货源信息 (goodsInfo)
+ if (options.goodsInfo) {
+ try {
+ const goodsInfo = JSON.parse(options.goodsInfo);
+ this.setData({
+ selectedGoods: goodsInfo,
+ 'goodsInfo.weight': goodsInfo.grossWeight || '',
+ 'goodsInfo.quantity': 1
+ });
+ } catch (e) {
+ console.error('解析货源信息失败:', e);
+ }
+ }
+
+ // 如果直接传递了货源信息 (goodsData)
+ if (options.goodsData) {
+ try {
+ // 尝试直接解析
+ let goodsData;
+ try {
+ goodsData = JSON.parse(options.goodsData);
+ } catch (e1) {
+ // 如果直接解析失败,尝试解码后再解析
+ try {
+ const decodedGoodsData = decodeURIComponent(options.goodsData);
+ goodsData = JSON.parse(decodedGoodsData);
+ } catch (e2) {
+ console.error('解析货源信息失败 (已尝试解码):', e2);
+ throw e2;
+ }
+ }
+
+ this.setData({
+ selectedGoods: goodsData,
+ 'goodsInfo.weight': goodsData.grossWeight || '',
+ 'goodsInfo.quantity': 1
+ });
+
+ // 设置出发地为商品所在地
+ if (goodsData.region) {
+ const regionInfo = this.parseRegion(goodsData.region);
+ this.setData({
+ 'origin.province': regionInfo.province || '',
+ 'origin.city': regionInfo.city || '',
+ 'origin.district': regionInfo.district || ''
+ });
+ }
+ } catch (e) {
+ console.error('解析货源信息失败:', e);
+ }
+ }
+ },
+
+ // 初始化地址选择器数据
+ initRegionData: function () {
+ // 这里可以从API获取地址数据,或者使用本地数据
+ const provinces = [
+ '北京市', '上海市', '广东省', '江苏省', '浙江省', '山东省', '河南省', '四川省', '湖北省', '福建省',
+ '湖南省', '安徽省', '河北省', '辽宁省', '陕西省', '江西省', '云南省', '黑龙江省', '山西省', '广西壮族自治区',
+ '内蒙古自治区', '吉林省', '贵州省', '新疆维吾尔自治区', '甘肃省', '重庆市', '宁夏回族自治区', '青海省', '西藏自治区', '海南省'
+ ];
+
+ this.setData({
+ 'regionList.provinces': provinces
+ });
+ },
+
+ // 加载商品信息
+ loadGoodsInfo: function (goodsId) {
+ // 从本地存储获取商品信息
+ const goods = wx.getStorageSync('goods') || [];
+ const goodsItem = goods.find(item => item.id === goodsId || item.productId === goodsId);
+
+ if (goodsItem) {
+ // 设置选中的货源信息
+ this.setData({
+ selectedGoods: goodsItem
+ });
+
+ // 设置出发地为商品所在地
+ if (goodsItem.region) {
+ const regionInfo = this.parseRegion(goodsItem.region);
+ this.setData({
+ 'origin.province': regionInfo.province || '',
+ 'origin.city': regionInfo.city || '',
+ 'origin.district': regionInfo.district || ''
+ });
+ }
+
+ // 设置货物重量(如果有)
+ if (goodsItem.grossWeight) {
+ this.setData({
+ 'goodsInfo.weight': goodsItem.grossWeight
+ });
+ }
+ }
+ },
+
+ // 解析地区信息
+ parseRegion: function (region) {
+ if (!region) return {};
+
+ // 简单的地区解析逻辑,实际项目中可能需要更复杂的解析
+ const parts = region.split(' ');
+ return {
+ province: parts[0] || '',
+ city: parts[1] || '',
+ district: parts[2] || ''
+ };
+ },
+
+ // 加载历史记录
+ loadHistoryRecords: function () {
+ const history = wx.getStorageSync('freightCalculatorHistory') || [];
+ this.setData({
+ historyRecords: history.slice(0, 10) // 只显示最近10条记录
+ });
+ },
+
+ // 保存历史记录
+ saveHistoryRecord: function (result) {
+ const history = wx.getStorageSync('freightCalculatorHistory') || [];
+ const record = {
+ id: Date.now(),
+ origin: this.data.origin,
+ destination: this.data.destination,
+ goodsInfo: this.data.goodsInfo,
+ vehicleInfo: this.data.vehicleInfo,
+ transportMode: this.data.transportMode,
+ result: result,
+ timestamp: new Date().toISOString()
+ };
+
+ // 添加到历史记录开头
+ history.unshift(record);
+ // 只保留最近20条记录
+ const newHistory = history.slice(0, 20);
+ wx.setStorageSync('freightCalculatorHistory', newHistory);
+
+ // 更新页面数据
+ this.setData({
+ historyRecords: newHistory.slice(0, 10)
+ });
+ },
+
+ // 输入出发地
+ bindOriginInput: function (e) {
+ const { key } = e.currentTarget.dataset;
+ this.setData({
+ [`origin.${key}`]: e.detail.value
+ });
+ },
+
+ // 输入目的地
+ bindDestinationInput: function (e) {
+ const { key } = e.currentTarget.dataset;
+ this.setData({
+ [`destination.${key}`]: e.detail.value
+ });
+ },
+
+ // 输入货物信息
+ bindGoodsInfoInput: function (e) {
+ const { key } = e.currentTarget.dataset;
+ this.setData({
+ [`goodsInfo.${key}`]: e.detail.value
+ });
+ },
+
+ // 选择规格组合
+ bindSpecChange: function (e) {
+ const index = e.detail.value;
+ this.setData({
+ selectedSpecIndex: index
+ });
+ },
+
+ // 选择车辆类型
+ 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
+ });
+ },
+
+
+
+
+
+ // 地址选择器值变化
+ bindRegionChange: function (e) {
+ const { type } = e.currentTarget.dataset;
+ const values = e.detail.value;
+
+ // 更新地址信息
+ if (type === 'origin') {
+ this.setData({
+ 'origin.province': values[0],
+ 'origin.city': values[1],
+ 'origin.district': values[2]
+ });
+ } else if (type === 'destination') {
+ this.setData({
+ 'destination.province': values[0],
+ 'destination.city': values[1],
+ 'destination.district': values[2]
+ });
+ }
+ },
+
+ // 打开出发地选择器
+ openOriginPicker: function () {
+ this.setData({
+ showOriginPicker: true
+ });
+ },
+
+ // 打开目的地选择器
+ openDestinationPicker: function () {
+ this.setData({
+ showDestinationPicker: true
+ });
+ },
+
+ // 关闭地址选择器
+ closePicker: function () {
+ this.setData({
+ showOriginPicker: false,
+ showDestinationPicker: false
+ });
+ },
+
+ // 选择地址
+ selectAddress: function (e) {
+ const { type, address } = e.detail;
+ if (type === 'origin') {
+ this.setData({
+ origin: address,
+ showOriginPicker: false
+ });
+ } else if (type === 'destination') {
+ this.setData({
+ destination: address,
+ showDestinationPicker: false
+ });
+ }
+ },
+
+ // 使用当前位置作为出发地
+ useCurrentLocation: function () {
+ const that = this;
+ wx.getLocation({
+ type: 'wgs84',
+ success: function (res) {
+ // 显示加载提示
+ wx.showLoading({
+ title: '获取地址中...',
+ mask: true
+ });
+
+ // 使用微信的地址解析服务获取详细地址
+ wx.request({
+ url: 'https://apis.map.qq.com/ws/geocoder/v1/',
+ data: {
+ location: `${res.latitude},${res.longitude}`,
+ key: 'OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77' // 这里使用腾讯地图API密钥,实际项目中应该替换为自己的密钥
+ },
+ success: function (response) {
+ wx.hideLoading();
+ if (response.data.status === 0) {
+ const result = response.data.result;
+ that.setData({
+ destination: {
+ province: result.address_component.province || '',
+ city: result.address_component.city || '',
+ district: result.address_component.district || '',
+ detail: result.address || '',
+ latitude: res.latitude,
+ longitude: res.longitude
+ }
+ });
+ wx.showToast({
+ title: '地址获取成功',
+ icon: 'success'
+ });
+ } else {
+ wx.showToast({
+ title: '地址解析失败,请手动输入',
+ icon: 'none'
+ });
+ }
+ },
+ fail: function (err) {
+ wx.hideLoading();
+ console.error('地址解析失败:', err);
+ wx.showToast({
+ title: '地址解析失败,请手动输入',
+ icon: 'none'
+ });
+ }
+ });
+ },
+ fail: function (err) {
+ console.error('获取位置失败:', err);
+ wx.showToast({
+ title: '获取位置失败,请手动输入',
+ icon: 'none'
+ });
+ }
+ });
+ },
+
+ // 手动选择出发地位置
+ chooseOriginLocation: function () {
+ const that = this;
+ wx.chooseLocation({
+ success(res) {
+ console.log('用户选择的出发地位置信息:', res);
+ const name = res.name;
+ const address = res.address;
+ const latitude = res.latitude;
+ const longitude = res.longitude;
+
+ // 使用腾讯地图API进行逆地理编码,获取详细地址信息
+ wx.request({
+ url: 'https://apis.map.qq.com/ws/geocoder/v1/',
+ data: {
+ location: `${latitude},${longitude}`,
+ key: 'OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77'
+ },
+ success: function (response) {
+ if (response.data.status === 0) {
+ const result = response.data.result;
+ that.setData({
+ origin: {
+ province: result.address_component.province || '',
+ city: result.address_component.city || '',
+ district: result.address_component.district || '',
+ detail: `${name} ${address}`,
+ latitude: latitude,
+ longitude: longitude
+ }
+ });
+ wx.showToast({
+ title: '出发地选择成功',
+ icon: 'success'
+ });
+ } else {
+ wx.showToast({
+ title: '地址解析失败,请手动输入',
+ icon: 'none'
+ });
+ }
+ },
+ fail: function (err) {
+ console.error('地址解析失败:', err);
+ wx.showToast({
+ title: '地址解析失败,请手动输入',
+ icon: 'none'
+ });
+ }
+ });
+ },
+ fail(err) {
+ console.error('选择出发地位置失败:', err);
+ if (err.errMsg === 'chooseLocation:fail auth deny') {
+ wx.showToast({
+ title: '需要位置授权才能选择地点',
+ icon: 'none',
+ duration: 2000
+ });
+ // 引导用户重新授权
+ that.requestLocationAuth();
+ } else {
+ wx.showToast({
+ title: '选择位置失败',
+ icon: 'none',
+ duration: 2000
+ });
+ }
+ }
+ });
+ },
+
+ // 手动选择目的地位置
+ chooseDestinationLocation: function () {
+ const that = this;
+ wx.chooseLocation({
+ success(res) {
+ console.log('用户选择的目的地位置信息:', res);
+ const name = res.name;
+ const address = res.address;
+ const latitude = res.latitude;
+ const longitude = res.longitude;
+
+ // 使用腾讯地图API进行逆地理编码,获取详细地址信息
+ wx.request({
+ url: 'https://apis.map.qq.com/ws/geocoder/v1/',
+ data: {
+ location: `${latitude},${longitude}`,
+ key: 'OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77'
+ },
+ success: function (response) {
+ if (response.data.status === 0) {
+ const result = response.data.result;
+ that.setData({
+ destination: {
+ province: result.address_component.province || '',
+ city: result.address_component.city || '',
+ district: result.address_component.district || '',
+ detail: `${name} ${address}`,
+ latitude: latitude,
+ longitude: longitude
+ }
+ });
+ wx.showToast({
+ title: '目的地选择成功',
+ icon: 'success'
+ });
+ } else {
+ wx.showToast({
+ title: '地址解析失败,请手动输入',
+ icon: 'none'
+ });
+ }
+ },
+ fail: function (err) {
+ console.error('地址解析失败:', err);
+ wx.showToast({
+ title: '地址解析失败,请手动输入',
+ icon: 'none'
+ });
+ }
+ });
+ },
+ fail(err) {
+ console.error('选择目的地位置失败:', err);
+ if (err.errMsg === 'chooseLocation:fail auth deny') {
+ wx.showToast({
+ title: '需要位置授权才能选择地点',
+ icon: 'none',
+ duration: 2000
+ });
+ // 引导用户重新授权
+ that.requestLocationAuth();
+ } else {
+ wx.showToast({
+ title: '选择位置失败',
+ icon: 'none',
+ duration: 2000
+ });
+ }
+ }
+ });
+ },
+
+ // 请求位置授权
+ requestLocationAuth: function () {
+ const that = this;
+
+ console.log('开始请求位置授权');
+
+ wx.showLoading({
+ title: '请求授权中...',
+ mask: true
+ });
+
+ // 检查用户是否已经拒绝过授权
+ wx.getSetting({
+ success(res) {
+ wx.hideLoading();
+ if (res.authSetting['scope.userLocation'] === false) {
+ // 用户已经拒绝过授权,直接引导到设置页面
+ wx.showModal({
+ title: '需要位置授权',
+ content: '请在设置中开启位置授权,以便我们为您提供相关服务',
+ showCancel: true,
+ cancelText: '取消',
+ confirmText: '去授权',
+ success: (res) => {
+ if (res.confirm) {
+ // 打开设置页面让用户手动开启授权
+ wx.openSetting({
+ success: (settingRes) => {
+ console.log('设置页面返回结果:', settingRes);
+ if (settingRes.authSetting['scope.userLocation']) {
+ // 用户在设置中开启了位置授权
+ wx.showToast({
+ title: '授权成功',
+ icon: 'success',
+ duration: 1500
+ });
+ } else {
+ // 用户在设置中仍未开启位置授权
+ wx.showToast({
+ title: '您已拒绝位置授权',
+ icon: 'none'
+ });
+ }
+ },
+ fail: (err) => {
+ console.error('打开设置失败:', err);
+ wx.showToast({
+ title: '打开设置失败',
+ icon: 'none'
+ });
+ }
+ });
+ }
+ }
+ });
+ } else {
+ // 用户未拒绝过授权,调用 authorize
+ wx.authorize({
+ scope: 'scope.userLocation',
+ success() {
+ console.log('位置授权成功');
+ wx.showToast({
+ title: '授权成功',
+ icon: 'success',
+ duration: 1500
+ });
+ },
+ fail(err) {
+ console.log('位置授权失败:', err);
+ // 授权失败,弹出模态框引导用户重新授权
+ wx.showModal({
+ title: '需要位置授权',
+ content: '请在设置中开启位置授权,以便我们为您提供相关服务',
+ showCancel: true,
+ cancelText: '取消',
+ confirmText: '去授权',
+ success: (res) => {
+ if (res.confirm) {
+ // 打开设置页面让用户手动开启授权
+ wx.openSetting({
+ success: (settingRes) => {
+ console.log('设置页面返回结果:', settingRes);
+ if (settingRes.authSetting['scope.userLocation']) {
+ // 用户在设置中开启了位置授权
+ wx.showToast({
+ title: '授权成功',
+ icon: 'success',
+ duration: 1500
+ });
+ } else {
+ // 用户在设置中仍未开启位置授权
+ wx.showToast({
+ title: '您已拒绝位置授权',
+ icon: 'none'
+ });
+ }
+ },
+ fail: (err) => {
+ console.error('打开设置失败:', err);
+ wx.showToast({
+ title: '打开设置失败',
+ icon: 'none'
+ });
+ }
+ });
+ }
+ }
+ });
+ }
+ });
+ }
+ },
+ fail(err) {
+ wx.hideLoading();
+ console.error('获取设置失败:', err);
+ wx.showToast({
+ title: '获取设置失败',
+ icon: 'none'
+ });
+ }
+ });
+ },
+
+ // 计算运费
+ calculateFreight: function () {
+ // 验证输入
+ if (!this.data.origin.province || !this.data.destination.province) {
+ wx.showToast({
+ title: '请填写完整的出发地和目的地',
+ icon: 'none'
+ });
+ return;
+ }
+
+ // 如果没有重量和体积,使用默认值 1
+ if (!this.data.goodsInfo.weight && !this.data.goodsInfo.volume) {
+ this.setData({
+ 'goodsInfo.weight': 1
+ });
+ }
+
+ 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) {
+ this.setData({ calculationResult: res.data });
+ // 保存历史记录
+ this.saveHistoryRecord(res.data);
+ } else {
+ wx.showToast({
+ title: res.message || '计算失败,请稍后重试',
+ icon: 'none'
+ });
+ }
+ }).catch(err => {
+ this.setData({ loading: false });
+ console.error('计算运费失败:', err);
+ wx.showToast({
+ title: '网络错误,请稍后重试',
+ icon: 'none'
+ });
+ });
+ },
+
+ // 清空输入
+ clearInput: function () {
+ this.setData({
+ origin: {
+ province: '',
+ city: '',
+ district: '',
+ detail: ''
+ },
+ destination: {
+ province: '',
+ city: '',
+ district: '',
+ detail: ''
+ },
+ goodsInfo: {
+ weight: '',
+ volume: '',
+ quantity: 1
+ },
+ vehicleInfo: {
+ type: '',
+ capacity: '',
+ number: '',
+ driver: '',
+ phone: ''
+ },
+ selectedVehicleTypeIndex: 0,
+
+ calculationResult: null
+ });
+ },
+
+ // 使用历史记录
+ useHistoryRecord: function (e) {
+ const index = e.currentTarget.dataset.index;
+ const record = this.data.historyRecords[index];
+
+ this.setData({
+ 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 });
+ }
+ }
+ },
+
+ // 删除历史记录
+ deleteHistoryRecord: function (e) {
+ const index = e.currentTarget.dataset.index;
+ const history = wx.getStorageSync('freightCalculatorHistory') || [];
+ history.splice(index, 1);
+ wx.setStorageSync('freightCalculatorHistory', history);
+
+ this.setData({
+ historyRecords: history.slice(0, 10)
+ });
+ },
+
+ // 清空历史记录
+ clearHistory: function () {
+ wx.setStorageSync('freightCalculatorHistory', []);
+ this.setData({
+ historyRecords: []
+ });
+ },
+
+ // 返回上一页
+ navigateBack: function () {
+ wx.navigateBack({
+ delta: 1
+ });
+ },
+
+ // 拨打电话
+ makePhoneCall: function (e) {
+ const phone = e.currentTarget.dataset.phone;
+ if (phone) {
+ wx.makePhoneCall({
+ phoneNumber: phone,
+ success: function () {
+ console.log('拨打电话成功');
+ },
+ fail: function (err) {
+ console.error('拨打电话失败:', err);
+ wx.showToast({
+ title: '拨打电话失败',
+ icon: 'none'
+ });
+ }
+ });
+ }
+ },
+
+});
\ No newline at end of file
diff --git a/pages/freight-calculator/index.json b/pages/freight-calculator/index.json
new file mode 100644
index 0000000..edd868a
--- /dev/null
+++ b/pages/freight-calculator/index.json
@@ -0,0 +1,5 @@
+{
+ "navigationBarTitleText": "物流运费估算",
+ "navigationBarBackgroundColor": "#ffffff",
+ "navigationBarTextStyle": "black"
+}
\ No newline at end of file
diff --git a/pages/freight-calculator/index.wxml b/pages/freight-calculator/index.wxml
new file mode 100644
index 0000000..8aa48b0
--- /dev/null
+++ b/pages/freight-calculator/index.wxml
@@ -0,0 +1,240 @@
+
+
+
+
+
+ 出发地
+
+
+ 省份
+
+
+
+ 城市
+
+
+
+ 区县
+
+
+
+ 详细地址
+
+
+
+ 快速选择
+
+
+ {{origin.province && origin.city && origin.district ? origin.province + origin.city + origin.district : '请选择'}}
+
+
+
+
+
+
+
+
+
+ 目的地
+
+
+ 省份
+
+
+
+ 城市
+
+
+
+ 区县
+
+
+
+ 详细地址
+
+
+
+ 快速选择
+
+
+ {{destination.province && destination.city && destination.district ? destination.province + destination.city + destination.district : '请选择'}}
+
+
+
+
+
+
+
+
+
+
+
+
+ 物流车辆信息
+
+
+ 车辆类型
+
+
+ {{vehicleTypes[selectedVehicleTypeIndex]}}
+
+
+
+
+ 载重能力 (吨)
+
+
+
+ 车辆编号
+
+
+
+ 司机姓名
+
+
+
+ 联系电话
+
+
+
+
+
+
+
+ 物流人员信息
+
+ 我们拥有专业的物流团队,为您提供高效、安全的运输服务。以下是我们的物流专家,他们具有丰富的行业经验和专业知识,能够为您的货物运输提供专业的解决方案。
+
+
+
+
+
+
+ {{item.description || '拥有丰富的物流行业经验,熟悉各种货物的运输要求和流程,能够为客户提供专业的物流解决方案和优质的服务。'}}
+
+
+
+
+ 联系电话:
+ {{item.phone}}
+
+
+
+
+
+ 暂无物流人员信息
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 计算结果
+
+
+ 预估运费
+ {{calculationResult.freight}} 元
+
+
+ 运输距离
+ {{calculationResult.distance}} km
+
+
+ 预计时效
+ {{calculationResult.deliveryTime}} 天
+
+
+
+
+
+
+ 历史记录
+
+
+
+ {{item.origin.province}}{{item.origin.city}}
+ →
+ {{item.destination.province}}{{item.destination.city}}
+ ¥{{item.result.freight}}
+
+
+
+
+
+
+
+
+
+
+
+
+ 历史记录
+
+ 暂无历史记录
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/freight-calculator/index.wxss b/pages/freight-calculator/index.wxss
new file mode 100644
index 0000000..636f2f6
--- /dev/null
+++ b/pages/freight-calculator/index.wxss
@@ -0,0 +1,877 @@
+/* pages/freight-calculator/index.wxss */
+/* 全局样式 */
+.container {
+ padding: 0;
+ background-color: #f8f9fa;
+ min-height: 100vh;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+ width: 100%;
+ max-width: 100%;
+ box-sizing: border-box;
+}
+
+/* 确保所有主要元素都有box-sizing */
+.main-content,
+.section,
+.form-item,
+.input,
+.location-btn,
+.picker,
+.calculate-section,
+.calculate-btn,
+.clear-btn,
+.result-section,
+.result-card,
+.result-item,
+.history-list,
+.history-item,
+.history-content,
+.history-actions,
+.clear-history-btn,
+.picker-overlay,
+.picker-popup-container,
+.picker-header,
+.picker-content,
+.empty-state,
+.divider {
+ box-sizing: border-box;
+}
+
+/* 物流人员信息样式 */
+.logistics-intro {
+ background-color: #f8f9fa;
+ border-radius: 12rpx;
+ padding: 16rpx;
+ margin-bottom: 20rpx;
+ border-left: 4rpx solid #1989fa;
+}
+
+.intro-text {
+ font-size: 22rpx;
+ color: #666;
+ line-height: 1.5;
+}
+
+.logistics-personnel {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ gap: 20rpx;
+}
+
+.personnel-card {
+ background-color: #fff;
+ border-radius: 16rpx;
+ padding: 24rpx;
+ box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.08);
+ border: 1rpx solid #e8e8e8;
+ width: 100%;
+ transition: all 0.3s ease;
+}
+
+.personnel-card:hover {
+ box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.12);
+ transform: translateY(-2rpx);
+ border-color: #1989fa;
+}
+
+.personnel-header-section {
+ display: flex;
+ align-items: flex-start;
+ margin-bottom: 20rpx;
+}
+
+.personnel-avatar {
+ width: 100rpx;
+ height: 100rpx;
+ border-radius: 50%;
+ background: linear-gradient(135deg, #1989fa 0%, #1890ff 100%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 24rpx;
+ flex-shrink: 0;
+ box-shadow: 0 4rpx 12rpx rgba(25, 137, 250, 0.3);
+}
+
+.avatar-text {
+ font-size: 40rpx;
+ font-weight: 700;
+ color: #fff;
+ text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
+}
+
+.personnel-header-info {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 8rpx;
+}
+
+.personnel-name {
+ font-size: 30rpx;
+ font-weight: 700;
+ color: #333;
+ line-height: 1.2;
+ margin: 0;
+}
+
+.personnel-position {
+ font-size: 22rpx;
+ color: #666;
+ margin: 0;
+ font-weight: 500;
+}
+
+.personnel-tags {
+ display: flex;
+ gap: 12rpx;
+ flex-wrap: wrap;
+ margin-top: 4rpx;
+}
+
+.personnel-tag {
+ font-size: 18rpx;
+ color: #1989fa;
+ background-color: rgba(25, 137, 250, 0.1);
+ padding: 6rpx 14rpx;
+ border-radius: 16rpx;
+ border: 1rpx solid rgba(25, 137, 250, 0.2);
+}
+
+.personnel-description {
+ margin-bottom: 20rpx;
+ padding: 16rpx;
+ background-color: #f8f9fa;
+ border-radius: 12rpx;
+ border-left: 4rpx solid #e3f2fd;
+}
+
+.description-text {
+ font-size: 22rpx;
+ color: #666;
+ line-height: 1.5;
+ margin: 0;
+}
+
+.personnel-contact-section {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-top: 20rpx;
+ border-top: 1rpx solid #f0f0f0;
+}
+
+.personnel-contact-info {
+ display: flex;
+ align-items: center;
+ gap: 8rpx;
+}
+
+.contact-label {
+ font-size: 22rpx;
+ color: #666;
+ font-weight: 500;
+}
+
+.contact-value {
+ font-size: 24rpx;
+ color: #333;
+ font-weight: 600;
+}
+
+.contact-btn {
+ background: linear-gradient(135deg, #1989fa 0%, #1890ff 100%);
+ color: #fff;
+ border: none;
+ border-radius: 8rpx;
+ padding: 14rpx 28rpx;
+ font-size: 22rpx;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 8rpx;
+ box-shadow: 0 4rpx 12rpx rgba(25, 137, 250, 0.3);
+ transition: all 0.3s ease;
+}
+
+.contact-btn:hover {
+ background: linear-gradient(135deg, #1890ff 0%, #177ddc 100%);
+ box-shadow: 0 6rpx 16rpx rgba(25, 137, 250, 0.4);
+ transform: translateY(-2rpx);
+}
+
+.contact-btn:active {
+ transform: translateY(0);
+ box-shadow: 0 2rpx 8rpx rgba(25, 137, 250, 0.3);
+}
+
+/* 主内容区域 */
+.main-content {
+ padding: 16rpx;
+ box-sizing: border-box;
+ width: 100%;
+}
+
+/* 区块样式 */
+.section {
+ background-color: #fff;
+ border-radius: 12rpx;
+ padding: 20rpx;
+ margin-bottom: 16rpx;
+ box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
+ width: 100%;
+}
+
+.section-title {
+ font-size: 24rpx;
+ font-weight: 600;
+ color: #333;
+ margin-bottom: 16rpx;
+ display: flex;
+ align-items: center;
+}
+
+.section-title::before {
+ content: '';
+ width: 6rpx;
+ height: 24rpx;
+ background-color: #1989fa;
+ border-radius: 3rpx;
+ margin-right: 10rpx;
+}
+
+/* 表单项目样式 */
+.form-item {
+ display: flex;
+ flex-direction: column;
+ margin-bottom: 16rpx;
+ position: relative;
+ width: 100%;
+}
+
+.label {
+ font-size: 22rpx;
+ color: #666;
+ margin-bottom: 8rpx;
+ font-weight: 500;
+}
+
+.input {
+ height: 68rpx;
+ border: 1rpx solid #e5e5e5;
+ border-radius: 8rpx;
+ padding: 0 20rpx;
+ font-size: 24rpx;
+ color: #333;
+ box-sizing: border-box;
+ line-height: 68rpx;
+ width: 100%;
+}
+
+.input:focus {
+ border-color: #1989fa;
+ outline: none;
+}
+
+/* 只读输入框 */
+.input.readonly {
+ background-color: #f8f9fa;
+ border-color: #e9ecef;
+ color: #6c757d;
+}
+
+/* 位置按钮 */
+.location-btn {
+ width: 100%;
+ height: 68rpx;
+ background-color: #f8f9fa;
+ border: 1rpx solid #e9ecef;
+ border-radius: 8rpx;
+ font-size: 22rpx;
+ color: #495057;
+ margin-top: 8rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-sizing: border-box;
+ line-height: 68rpx;
+ flex: 1;
+}
+
+.location-btn::before {
+ content: '📍';
+ margin-right: 8rpx;
+ font-size: 24rpx;
+}
+
+/* 选择器样式 */
+.picker {
+ height: 68rpx;
+ border: 1rpx solid #e5e5e5;
+ border-radius: 8rpx;
+ padding: 0 20rpx;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ background-color: #fff;
+ width: 100%;
+ font-size: 24rpx;
+ color: #333;
+ position: relative;
+ box-sizing: border-box;
+ line-height: 68rpx;
+}
+
+.picker::after {
+ content: '▼';
+ position: absolute;
+ right: 20rpx;
+ top: 50%;
+ transform: translateY(-50%);
+ font-size: 16rpx;
+ color: #666;
+}
+
+/* 计算按钮区域 */
+.calculate-section {
+ display: flex;
+ flex-direction: column;
+ margin-top: 24rpx;
+ margin-bottom: 24rpx;
+ gap: 12rpx;
+ width: 100%;
+}
+
+.calculate-btn {
+ width: 100%;
+ height: 76rpx;
+ background-color: #1989fa;
+ color: #fff;
+ border: none;
+ border-radius: 8rpx;
+ font-size: 26rpx;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-sizing: border-box;
+ line-height: 76rpx;
+}
+
+.clear-btn {
+ width: 100%;
+ height: 76rpx;
+ background-color: #fff;
+ color: #666;
+ border: 1rpx solid #e5e5e5;
+ border-radius: 8rpx;
+ font-size: 26rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-sizing: border-box;
+ line-height: 76rpx;
+}
+
+/* 结果区域 */
+.result-section {
+ background-color: #fff;
+ border-radius: 12rpx;
+ padding: 20rpx;
+ margin-bottom: 16rpx;
+ box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
+ width: 100%;
+}
+
+.result-card {
+ background: linear-gradient(135deg, #f0f9ff 0%, #e3f2fd 100%);
+ border-radius: 8rpx;
+ padding: 20rpx;
+ border-left: 4rpx solid #1989fa;
+ width: 100%;
+}
+
+.result-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 12rpx;
+ padding: 8rpx 0;
+ border-bottom: 1rpx solid rgba(0, 0, 0, 0.05);
+ width: 100%;
+}
+
+.result-item:last-child {
+ margin-bottom: 0;
+ border-bottom: none;
+}
+
+.result-label {
+ font-size: 22rpx;
+ color: #666;
+ font-weight: 500;
+ flex: 1;
+}
+
+.result-value {
+ font-size: 24rpx;
+ font-weight: 600;
+ color: #333;
+ flex: 1;
+ text-align: right;
+}
+
+.result-value.freight {
+ color: #ff4d4f;
+ font-size: 28rpx;
+}
+
+/* 历史记录 */
+.history-list {
+ margin-top: 12rpx;
+ width: 100%;
+}
+
+.history-item {
+ background-color: #f8f9fa;
+ border-radius: 8rpx;
+ padding: 16rpx;
+ margin-bottom: 10rpx;
+ border-left: 4rpx solid #e9ecef;
+ width: 100%;
+}
+
+.history-content {
+ display: flex;
+ align-items: center;
+ margin-bottom: 8rpx;
+ flex-wrap: wrap;
+ width: 100%;
+}
+
+.history-origin {
+ font-size: 22rpx;
+ color: #333;
+ font-weight: 500;
+ margin-right: 8rpx;
+ flex: 0 0 auto;
+}
+
+.history-arrow {
+ font-size: 22rpx;
+ color: #999;
+ margin: 0 8rpx;
+ flex: 0 0 auto;
+}
+
+.history-destination {
+ font-size: 22rpx;
+ color: #333;
+ font-weight: 500;
+ margin-right: 16rpx;
+ flex: 1;
+ min-width: 120rpx;
+}
+
+.history-freight {
+ font-size: 24rpx;
+ font-weight: 600;
+ color: #ff4d4f;
+ margin-left: auto;
+ flex: 0 0 auto;
+}
+
+.history-actions {
+ display: flex;
+ justify-content: flex-end;
+ gap: 12rpx;
+ margin-top: 8rpx;
+ width: 100%;
+}
+
+.history-use-btn {
+ font-size: 20rpx;
+ color: #1989fa;
+ background: none;
+ border: none;
+ padding: 0 8rpx;
+}
+
+.history-delete-btn {
+ font-size: 20rpx;
+ color: #ff4d4f;
+ background: none;
+ border: none;
+ padding: 0 8rpx;
+}
+
+.clear-history-btn {
+ width: 100%;
+ height: 56rpx;
+ background: none;
+ border: 1rpx solid #e5e5e5;
+ border-radius: 8rpx;
+ font-size: 22rpx;
+ color: #666;
+ margin-top: 12rpx;
+ box-sizing: border-box;
+}
+
+/* 选择器弹窗 */
+.picker-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+ display: flex;
+ justify-content: center;
+ align-items: flex-end;
+ z-index: 1000;
+ width: 100%;
+}
+
+.picker-popup-container {
+ background-color: #fff;
+ border-radius: 16rpx 16rpx 0 0;
+ width: 100%;
+ max-height: 70vh;
+ overflow: hidden;
+}
+
+.picker-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20rpx;
+ border-bottom: 1rpx solid #e5e5e5;
+ background-color: #f8f9fa;
+ width: 100%;
+}
+
+.picker-title {
+ font-size: 26rpx;
+ font-weight: 600;
+ color: #333;
+ flex: 1;
+ text-align: center;
+}
+
+.picker-close {
+ font-size: 28rpx;
+ color: #666;
+ background: none;
+ border: none;
+ padding: 16rpx;
+ width: 40rpx;
+ height: 40rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 50%;
+ position: absolute;
+ right: 20rpx;
+}
+
+.picker-content {
+ padding: 24rpx;
+ text-align: center;
+ font-size: 24rpx;
+ color: #666;
+ width: 100%;
+}
+
+/* 空状态 */
+.empty-state {
+ text-align: center;
+ padding: 48rpx 16rpx;
+ color: #999;
+ font-size: 22rpx;
+ width: 100%;
+}
+
+.empty-state::before {
+ content: '📦';
+ font-size: 48rpx;
+ display: block;
+ margin-bottom: 16rpx;
+}
+
+/* 分隔线 */
+.divider {
+ height: 1rpx;
+ background-color: #e5e5e5;
+ margin: 16rpx 0;
+ width: 100%;
+}
+
+/* 标签样式 */
+.tag {
+ display: inline-block;
+ padding: 4rpx 12rpx;
+ border-radius: 12rpx;
+ font-size: 20rpx;
+ font-weight: 500;
+ margin-right: 8rpx;
+ margin-bottom: 8rpx;
+}
+
+.tag-primary {
+ background-color: #e6f7ff;
+ color: #1890ff;
+}
+
+.tag-success {
+ background-color: #f6ffed;
+ color: #52c41a;
+}
+
+.tag-warning {
+ background-color: #fff7e6;
+ color: #fa8c16;
+}
+
+.tag-danger {
+ background-color: #fff1f0;
+ color: #ff4d4f;
+}
+
+/* 动画效果 */
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(16rpx);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes slideUp {
+ from {
+ transform: translateY(100%);
+ }
+ to {
+ transform: translateY(0);
+ }
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+/* 响应式调整 */
+@media (min-width: 375px) {
+ .main-content {
+ padding: 20rpx;
+ }
+
+ .section {
+ padding: 24rpx;
+ margin-bottom: 20rpx;
+ }
+
+ .section-title {
+ font-size: 26rpx;
+ margin-bottom: 20rpx;
+ }
+
+ .form-item {
+ margin-bottom: 20rpx;
+ }
+
+ .input {
+ height: 72rpx;
+ font-size: 26rpx;
+ padding: 0 24rpx;
+ }
+
+ .picker {
+ height: 72rpx;
+ font-size: 26rpx;
+ padding: 0 24rpx;
+ }
+
+ .location-btn {
+ height: 72rpx;
+ font-size: 24rpx;
+ }
+
+ .calculate-btn,
+ .clear-btn {
+ height: 80rpx;
+ font-size: 28rpx;
+ }
+
+ .result-item {
+ margin-bottom: 16rpx;
+ padding: 12rpx 0;
+ }
+
+ .result-label {
+ font-size: 24rpx;
+ }
+
+ .result-value {
+ font-size: 26rpx;
+ }
+
+ .result-value.freight {
+ font-size: 32rpx;
+ }
+
+ .history-content {
+ margin-bottom: 12rpx;
+ }
+
+ .history-origin,
+ .history-destination,
+ .history-arrow {
+ font-size: 24rpx;
+ }
+
+ .history-freight {
+ font-size: 26rpx;
+ }
+
+ .history-use-btn,
+ .history-delete-btn {
+ font-size: 22rpx;
+ }
+
+ .clear-history-btn {
+ height: 60rpx;
+ font-size: 24rpx;
+ }
+
+ .empty-state {
+ padding: 64rpx 24rpx;
+ font-size: 24rpx;
+ }
+
+ .empty-state::before {
+ font-size: 64rpx;
+ margin-bottom: 24rpx;
+ }
+
+ .divider {
+ margin: 24rpx 0;
+ }
+}
+
+/* 大屏幕设备调整 */
+@media (min-width: 750rpx) {
+ .container {
+ max-width: 750rpx;
+ margin: 0 auto;
+ }
+
+ .header {
+ max-width: 750rpx;
+ margin: 0 auto;
+ }
+}
+
+/* 小屏幕设备调整 */
+@media (max-width: 320px) {
+ .main-content {
+ padding: 12rpx;
+ }
+
+ .section {
+ padding: 16rpx;
+ margin-bottom: 12rpx;
+ }
+
+ .section-title {
+ font-size: 22rpx;
+ margin-bottom: 12rpx;
+ }
+
+ .input {
+ height: 64rpx;
+ font-size: 22rpx;
+ padding: 0 16rpx;
+ }
+
+ .picker {
+ height: 64rpx;
+ font-size: 22rpx;
+ padding: 0 16rpx;
+ }
+
+ .location-btn {
+ height: 64rpx;
+ font-size: 20rpx;
+ }
+
+ .calculate-btn,
+ .clear-btn {
+ height: 72rpx;
+ font-size: 24rpx;
+ }
+
+ .result-item {
+ margin-bottom: 8rpx;
+ padding: 6rpx 0;
+ }
+
+ .result-label {
+ font-size: 20rpx;
+ }
+
+ .result-value {
+ font-size: 22rpx;
+ }
+
+ .result-value.freight {
+ font-size: 26rpx;
+ }
+
+ .history-content {
+ margin-bottom: 6rpx;
+ }
+
+ .history-origin,
+ .history-destination,
+ .history-arrow {
+ font-size: 20rpx;
+ }
+
+ .history-freight {
+ font-size: 22rpx;
+ }
+
+ .history-use-btn,
+ .history-delete-btn {
+ font-size: 18rpx;
+ }
+
+ .clear-history-btn {
+ height: 52rpx;
+ font-size: 20rpx;
+ }
+
+ .empty-state {
+ padding: 32rpx 12rpx;
+ font-size: 20rpx;
+ }
+
+ .empty-state::before {
+ font-size: 32rpx;
+ margin-bottom: 12rpx;
+ }
+
+ .divider {
+ margin: 12rpx 0;
+ }
+}
\ No newline at end of file
diff --git a/pages/goods-detail/goods-detail.js b/pages/goods-detail/goods-detail.js
index 604a537..4f90d30 100644
--- a/pages/goods-detail/goods-detail.js
+++ b/pages/goods-detail/goods-detail.js
@@ -1424,6 +1424,60 @@ Page({
return true;
},
+ // 导航到运费计算器
+ navigateToFreightCalculator: function() {
+ const goodsDetail = this.data.goodsDetail;
+ if (!goodsDetail) {
+ wx.showToast({
+ title: '商品信息加载中',
+ icon: 'none'
+ });
+ return;
+ }
+
+ // 构建要传递的商品信息
+ const selectedGoods = {
+ id: goodsDetail.id || goodsDetail.productId,
+ name: goodsDetail.name || '未命名商品',
+ region: goodsDetail.region || '',
+ specification: goodsDetail.specification || goodsDetail.spec || '',
+ quantity: goodsDetail.quantity || goodsDetail.minOrder || '',
+ price: goodsDetail.price || '',
+ producting: goodsDetail.producting || '',
+ yolk: goodsDetail.yolk || '',
+ freshness: goodsDetail.freshness || '',
+ weightQuantityData: goodsDetail.weightQuantityData || [],
+ imageUrl: (goodsDetail.imageUrls && goodsDetail.imageUrls.length > 0) ? goodsDetail.imageUrls[0] : ''
+ };
+
+ // 导航到运费计算器页面
+ this.navigateLock(() => {
+ try {
+ const goodsDataString = JSON.stringify(selectedGoods);
+ const encodedGoodsData = encodeURIComponent(goodsDataString);
+ wx.navigateTo({
+ url: `/pages/freight-calculator/index?goodsData=${encodedGoodsData}`,
+ success: function() {
+ console.log('成功跳转到运费计算器页面');
+ },
+ fail: function(error) {
+ console.error('跳转到运费计算器页面失败:', error);
+ wx.showToast({
+ title: '跳转失败,请稍后重试',
+ icon: 'none'
+ });
+ }
+ });
+ } catch (error) {
+ console.error('构建跳转参数失败:', error);
+ wx.showToast({
+ title: '参数错误,请稍后重试',
+ icon: 'none'
+ });
+ }
+ });
+ },
+
// 点击对比价格列表中的商品,跳转到对应的商品详情页
viewCompareGoodsDetail: function(e) {
const item = e.currentTarget.dataset.item;
diff --git a/pages/goods-detail/goods-detail.wxml b/pages/goods-detail/goods-detail.wxml
index 264d60b..ffde07e 100644
--- a/pages/goods-detail/goods-detail.wxml
+++ b/pages/goods-detail/goods-detail.wxml
@@ -461,6 +461,13 @@
>
还价
+